mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-10 04:37:37 +01:00
Switched to a custom logging lib instead of that spdlog junk
This commit is contained in:
parent
7e80bbd02c
commit
7723d15e8f
@ -47,10 +47,10 @@ namespace backend {
|
|||||||
void handleAppCmd(struct android_app* app, int32_t appCmd) {
|
void handleAppCmd(struct android_app* app, int32_t appCmd) {
|
||||||
switch (appCmd) {
|
switch (appCmd) {
|
||||||
case APP_CMD_SAVE_STATE:
|
case APP_CMD_SAVE_STATE:
|
||||||
spdlog::warn("APP_CMD_SAVE_STATE");
|
flog::warn("APP_CMD_SAVE_STATE");
|
||||||
break;
|
break;
|
||||||
case APP_CMD_INIT_WINDOW:
|
case APP_CMD_INIT_WINDOW:
|
||||||
spdlog::warn("APP_CMD_INIT_WINDOW");
|
flog::warn("APP_CMD_INIT_WINDOW");
|
||||||
if (pauseRendering && !exited) {
|
if (pauseRendering && !exited) {
|
||||||
doPartialInit();
|
doPartialInit();
|
||||||
pauseRendering = false;
|
pauseRendering = false;
|
||||||
@ -58,15 +58,15 @@ namespace backend {
|
|||||||
exited = false;
|
exited = false;
|
||||||
break;
|
break;
|
||||||
case APP_CMD_TERM_WINDOW:
|
case APP_CMD_TERM_WINDOW:
|
||||||
spdlog::warn("APP_CMD_TERM_WINDOW");
|
flog::warn("APP_CMD_TERM_WINDOW");
|
||||||
pauseRendering = true;
|
pauseRendering = true;
|
||||||
backend::end();
|
backend::end();
|
||||||
break;
|
break;
|
||||||
case APP_CMD_GAINED_FOCUS:
|
case APP_CMD_GAINED_FOCUS:
|
||||||
spdlog::warn("APP_CMD_GAINED_FOCUS");
|
flog::warn("APP_CMD_GAINED_FOCUS");
|
||||||
break;
|
break;
|
||||||
case APP_CMD_LOST_FOCUS:
|
case APP_CMD_LOST_FOCUS:
|
||||||
spdlog::warn("APP_CMD_LOST_FOCUS");
|
flog::warn("APP_CMD_LOST_FOCUS");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ namespace backend {
|
|||||||
|
|
||||||
int aquireWindow() {
|
int aquireWindow() {
|
||||||
while (!app->window) {
|
while (!app->window) {
|
||||||
spdlog::warn("Waiting on the shitty window thing"); std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
flog::warn("Waiting on the shitty window thing"); std::this_thread::sleep_for(std::chrono::milliseconds(30));
|
||||||
int out_events;
|
int out_events;
|
||||||
struct android_poll_source* out_data;
|
struct android_poll_source* out_data;
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ namespace backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int init(std::string resDir) {
|
int init(std::string resDir) {
|
||||||
spdlog::warn("Backend init");
|
flog::warn("Backend init");
|
||||||
|
|
||||||
// Get window
|
// Get window
|
||||||
aquireWindow();
|
aquireWindow();
|
||||||
@ -174,7 +174,7 @@ namespace backend {
|
|||||||
void setMouseScreenPos(double x, double y) {}
|
void setMouseScreenPos(double x, double y) {}
|
||||||
|
|
||||||
int renderLoop() {
|
int renderLoop() {
|
||||||
spdlog::warn("BRUH: {0}", (void*)backend::app->window);
|
flog::warn("BRUH: {0}", (void*)backend::app->window);
|
||||||
while (true) {
|
while (true) {
|
||||||
int out_events;
|
int out_events;
|
||||||
struct android_poll_source* out_data;
|
struct android_poll_source* out_data;
|
||||||
@ -185,7 +185,7 @@ namespace backend {
|
|||||||
|
|
||||||
// Exit the app by returning from within the infinite loop
|
// Exit the app by returning from within the infinite loop
|
||||||
if (app->destroyRequested != 0) {
|
if (app->destroyRequested != 0) {
|
||||||
spdlog::warn("ASKED TO EXIT");
|
flog::warn("ASKED TO EXIT");
|
||||||
exited = true;
|
exited = true;
|
||||||
|
|
||||||
// Stop SDR
|
// Stop SDR
|
||||||
@ -465,7 +465,7 @@ extern "C" {
|
|||||||
|
|
||||||
// Check if this is the first time we run or not
|
// Check if this is the first time we run or not
|
||||||
if (backend::initialized) {
|
if (backend::initialized) {
|
||||||
spdlog::warn("android_main called again");
|
flog::warn("android_main called again");
|
||||||
backend::doPartialInit();
|
backend::doPartialInit();
|
||||||
backend::pauseRendering = false;
|
backend::pauseRendering = false;
|
||||||
backend::renderLoop();
|
backend::renderLoop();
|
||||||
@ -474,9 +474,9 @@ extern "C" {
|
|||||||
backend::initialized = true;
|
backend::initialized = true;
|
||||||
|
|
||||||
// prepare spdlog
|
// prepare spdlog
|
||||||
auto console_sink = std::make_shared<spdlog::sinks::android_sink_st>("SDR++");
|
auto console_sink = std::make_shared<flog::sinks::android_sink_st>("SDR++");
|
||||||
auto logger = std::shared_ptr<spdlog::logger>(new spdlog::logger("", { console_sink }));
|
auto logger = std::shared_ptr<flog::logger>(new flog::logger("", { console_sink }));
|
||||||
spdlog::set_default_logger(logger);
|
flog::set_default_logger(logger);
|
||||||
|
|
||||||
// Grab files dir
|
// Grab files dir
|
||||||
std::string appdir = backend::getAppFilesDir();
|
std::string appdir = backend::getAppFilesDir();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "imgui_impl_glfw.h"
|
#include "imgui_impl_glfw.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <utils/opengl_include_code.h>
|
#include <utils/opengl_include_code.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
@ -50,7 +50,7 @@ namespace backend {
|
|||||||
GLFWmonitor* monitor;
|
GLFWmonitor* monitor;
|
||||||
|
|
||||||
static void glfw_error_callback(int error, const char* description) {
|
static void glfw_error_callback(int error, const char* description) {
|
||||||
spdlog::error("Glfw Error {0}: {1}", error, description);
|
flog::error("Glfw Error {0}: {1}", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void maximized_callback(GLFWwindow* window, int n) {
|
static void maximized_callback(GLFWwindow* window, int n) {
|
||||||
@ -104,10 +104,10 @@ namespace backend {
|
|||||||
monitor = glfwGetPrimaryMonitor();
|
monitor = glfwGetPrimaryMonitor();
|
||||||
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
|
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
|
||||||
if (window == NULL) {
|
if (window == NULL) {
|
||||||
spdlog::info("OpenGL {0}.{1} {2}was not supported", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? "ES " : "");
|
flog::info("OpenGL {0}.{1} {2}was not supported", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? "ES " : "");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
spdlog::info("Using OpenGL {0}.{1}{2}", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? " ES" : "");
|
flog::info("Using OpenGL {0}.{1}{2}", OPENGL_VERSIONS_MAJOR[i], OPENGL_VERSIONS_MINOR[i], OPENGL_VERSIONS_IS_ES[i] ? " ES" : "");
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(window);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ namespace backend {
|
|||||||
|
|
||||||
// Load app icon
|
// Load app icon
|
||||||
if (!std::filesystem::is_regular_file(resDir + "/icons/sdrpp.png")) {
|
if (!std::filesystem::is_regular_file(resDir + "/icons/sdrpp.png")) {
|
||||||
spdlog::error("Icon file '{0}' doesn't exist!", resDir + "/icons/sdrpp.png");
|
flog::error("Icon file '{0}' doesn't exist!", resDir + "/icons/sdrpp.png");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,9 +176,9 @@ namespace backend {
|
|||||||
|
|
||||||
if (!ImGui_ImplOpenGL3_Init(glsl_version)) {
|
if (!ImGui_ImplOpenGL3_Init(glsl_version)) {
|
||||||
// If init fail, try to fall back on GLSL 1.2
|
// If init fail, try to fall back on GLSL 1.2
|
||||||
spdlog::warn("Could not init using OpenGL with normal GLSL version, falling back to GLSL 1.2");
|
flog::warn("Could not init using OpenGL with normal GLSL version, falling back to GLSL 1.2");
|
||||||
if (!ImGui_ImplOpenGL3_Init("#version 120")) {
|
if (!ImGui_ImplOpenGL3_Init("#version 120")) {
|
||||||
spdlog::error("Failed to initialize OpenGL with GLSL 1.2");
|
flog::error("Failed to initialize OpenGL with GLSL 1.2");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +186,7 @@ namespace backend {
|
|||||||
// Set window size and fullscreen state
|
// Set window size and fullscreen state
|
||||||
glfwGetWindowSize(window, &_winWidth, &_winHeight);
|
glfwGetWindowSize(window, &_winWidth, &_winHeight);
|
||||||
if (fullScreen) {
|
if (fullScreen) {
|
||||||
spdlog::info("Fullscreen: ON");
|
flog::info("Fullscreen: ON");
|
||||||
fsWidth = _winWidth;
|
fsWidth = _winWidth;
|
||||||
fsHeight = _winHeight;
|
fsHeight = _winHeight;
|
||||||
glfwGetWindowPos(window, &fsPosX, &fsPosY);
|
glfwGetWindowPos(window, &fsPosX, &fsPosY);
|
||||||
@ -251,7 +251,7 @@ namespace backend {
|
|||||||
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
|
if (ImGui::IsKeyPressed(GLFW_KEY_F11)) {
|
||||||
fullScreen = !fullScreen;
|
fullScreen = !fullScreen;
|
||||||
if (fullScreen) {
|
if (fullScreen) {
|
||||||
spdlog::info("Fullscreen: ON");
|
flog::info("Fullscreen: ON");
|
||||||
fsWidth = _winWidth;
|
fsWidth = _winWidth;
|
||||||
fsHeight = _winHeight;
|
fsHeight = _winHeight;
|
||||||
glfwGetWindowPos(window, &fsPosX, &fsPosY);
|
glfwGetWindowPos(window, &fsPosX, &fsPosY);
|
||||||
@ -262,7 +262,7 @@ namespace backend {
|
|||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::info("Fullscreen: OFF");
|
flog::info("Fullscreen: OFF");
|
||||||
glfwSetWindowMonitor(window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
|
glfwSetWindowMonitor(window, nullptr, fsPosX, fsPosY, fsWidth, fsHeight, 0);
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["fullscreen"] = false;
|
core::configManager.conf["fullscreen"] = false;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -18,16 +18,16 @@ void ConfigManager::setPath(std::string file) {
|
|||||||
void ConfigManager::load(json def, bool lock) {
|
void ConfigManager::load(json def, bool lock) {
|
||||||
if (lock) { mtx.lock(); }
|
if (lock) { mtx.lock(); }
|
||||||
if (path == "") {
|
if (path == "") {
|
||||||
spdlog::error("Config manager tried to load file with no path specified");
|
flog::error("Config manager tried to load file with no path specified");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
spdlog::warn("Config file '{0}' does not exist, creating it", path);
|
flog::warn("Config file '{0}' does not exist, creating it", path);
|
||||||
conf = def;
|
conf = def;
|
||||||
save(false);
|
save(false);
|
||||||
}
|
}
|
||||||
if (!std::filesystem::is_regular_file(path)) {
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
spdlog::error("Config file '{0}' isn't a file", path);
|
flog::error("Config file '{0}' isn't a file", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ void ConfigManager::load(json def, bool lock) {
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
catch (std::exception e) {
|
catch (std::exception e) {
|
||||||
spdlog::error("Config file '{0}' is corrupted, resetting it", path);
|
flog::error("Config file '{0}' is corrupted, resetting it", path);
|
||||||
conf = def;
|
conf = def;
|
||||||
save(false);
|
save(false);
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ void ConfigManager::release(bool modified) {
|
|||||||
void ConfigManager::autoSaveWorker() {
|
void ConfigManager::autoSaveWorker() {
|
||||||
while (autoSaveEnabled) {
|
while (autoSaveEnabled) {
|
||||||
if (!mtx.try_lock()) {
|
if (!mtx.try_lock()) {
|
||||||
spdlog::warn("ConfigManager locked, waiting...");
|
flog::warn("ConfigManager locked, waiting...");
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/icons.h>
|
#include <gui/icons.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <gui/widgets/bandplan.h>
|
#include <gui/widgets/bandplan.h>
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
@ -53,13 +53,13 @@ namespace core {
|
|||||||
gui::mainWindow.setViewBandwidthSlider(1.0);
|
gui::mainWindow.setViewBandwidthSlider(1.0);
|
||||||
|
|
||||||
// Debug logs
|
// Debug logs
|
||||||
spdlog::info("New DSP samplerate: {0} (source samplerate is {1})", effectiveSr, samplerate);
|
flog::info("New DSP samplerate: {0} (source samplerate is {1})", effectiveSr, samplerate);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// main
|
// main
|
||||||
int sdrpp_main(int argc, char* argv[]) {
|
int sdrpp_main(int argc, char* argv[]) {
|
||||||
spdlog::info("SDR++ v" VERSION_STR);
|
flog::info("SDR++ v" VERSION_STR);
|
||||||
|
|
||||||
#ifdef IS_MACOS_BUNDLE
|
#ifdef IS_MACOS_BUNDLE
|
||||||
// If this is a MacOS .app, CD to the correct directory
|
// If this is a MacOS .app, CD to the correct directory
|
||||||
@ -90,16 +90,16 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
// Check root directory
|
// Check root directory
|
||||||
std::string root = (std::string)core::args["root"];
|
std::string root = (std::string)core::args["root"];
|
||||||
if (!std::filesystem::exists(root)) {
|
if (!std::filesystem::exists(root)) {
|
||||||
spdlog::warn("Root directory {0} does not exist, creating it", root);
|
flog::warn("Root directory {0} does not exist, creating it", root);
|
||||||
if (!std::filesystem::create_directories(root)) {
|
if (!std::filesystem::create_directories(root)) {
|
||||||
spdlog::error("Could not create root directory {0}", root);
|
flog::error("Could not create root directory {0}", root);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the path actually is a directory
|
// Check that the path actually is a directory
|
||||||
if (!std::filesystem::is_directory(root)) {
|
if (!std::filesystem::is_directory(root)) {
|
||||||
spdlog::error("{0} is not a directory", root);
|
flog::error("{0} is not a directory", root);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
spdlog::info("Loading config");
|
flog::info("Loading config");
|
||||||
core::configManager.setPath(root + "/config.json");
|
core::configManager.setPath(root + "/config.json");
|
||||||
core::configManager.load(defConfig);
|
core::configManager.load(defConfig);
|
||||||
core::configManager.enableAutoSave();
|
core::configManager.enableAutoSave();
|
||||||
@ -295,7 +295,7 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
// Fix missing elements in config
|
// Fix missing elements in config
|
||||||
for (auto const& item : defConfig.items()) {
|
for (auto const& item : defConfig.items()) {
|
||||||
if (!core::configManager.conf.contains(item.key())) {
|
if (!core::configManager.conf.contains(item.key())) {
|
||||||
spdlog::info("Missing key in config {0}, repairing", item.key());
|
flog::info("Missing key in config {0}, repairing", item.key());
|
||||||
core::configManager.conf[item.key()] = defConfig[item.key()];
|
core::configManager.conf[item.key()] = defConfig[item.key()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,7 +304,7 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
auto items = core::configManager.conf.items();
|
auto items = core::configManager.conf.items();
|
||||||
for (auto const& item : items) {
|
for (auto const& item : items) {
|
||||||
if (!defConfig.contains(item.key())) {
|
if (!defConfig.contains(item.key())) {
|
||||||
spdlog::info("Unused key in config {0}, repairing", item.key());
|
flog::info("Unused key in config {0}, repairing", item.key());
|
||||||
core::configManager.conf.erase(item.key());
|
core::configManager.conf.erase(item.key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,7 +334,7 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
// Assert that the resource directory is absolute and check existence
|
// Assert that the resource directory is absolute and check existence
|
||||||
resDir = std::filesystem::absolute(resDir).string();
|
resDir = std::filesystem::absolute(resDir).string();
|
||||||
if (!std::filesystem::is_directory(resDir)) {
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
spdlog::error("Resource directory doesn't exist! Please make sure that you've configured it correctly in config.json (check readme for details)");
|
flog::error("Resource directory doesn't exist! Please make sure that you've configured it correctly in config.json (check readme for details)");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,20 +350,20 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
LoadingScreen::init();
|
LoadingScreen::init();
|
||||||
|
|
||||||
LoadingScreen::show("Loading icons");
|
LoadingScreen::show("Loading icons");
|
||||||
spdlog::info("Loading icons");
|
flog::info("Loading icons");
|
||||||
if (!icons::load(resDir)) { return -1; }
|
if (!icons::load(resDir)) { return -1; }
|
||||||
|
|
||||||
LoadingScreen::show("Loading band plans");
|
LoadingScreen::show("Loading band plans");
|
||||||
spdlog::info("Loading band plans");
|
flog::info("Loading band plans");
|
||||||
bandplan::loadFromDir(resDir + "/bandplans");
|
bandplan::loadFromDir(resDir + "/bandplans");
|
||||||
|
|
||||||
LoadingScreen::show("Loading band plan colors");
|
LoadingScreen::show("Loading band plan colors");
|
||||||
spdlog::info("Loading band plans color table");
|
flog::info("Loading band plans color table");
|
||||||
bandplan::loadColorTable(bandColors);
|
bandplan::loadColorTable(bandColors);
|
||||||
|
|
||||||
gui::mainWindow.init();
|
gui::mainWindow.init();
|
||||||
|
|
||||||
spdlog::info("Ready.");
|
flog::info("Ready.");
|
||||||
|
|
||||||
// Run render loop (TODO: CHECK RETURN VALUE)
|
// Run render loop (TODO: CHECK RETURN VALUE)
|
||||||
backend::renderLoop();
|
backend::renderLoop();
|
||||||
@ -384,6 +384,6 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
core::configManager.save();
|
core::configManager.save();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
spdlog::info("Exiting successfully");
|
flog::info("Exiting successfully");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ namespace sdrpp_credits {
|
|||||||
"fftw3 (fftw.org)",
|
"fftw3 (fftw.org)",
|
||||||
"glfw (Camilla Löwy)",
|
"glfw (Camilla Löwy)",
|
||||||
"json (nlohmann)",
|
"json (nlohmann)",
|
||||||
"spdlog (gabime)",
|
|
||||||
"Portable File Dialogs"
|
"Portable File Dialogs"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ namespace dsp::buffer {
|
|||||||
writeCur = ((writeCur) % TEST_BUFFER_SIZE);
|
writeCur = ((writeCur) % TEST_BUFFER_SIZE);
|
||||||
|
|
||||||
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
|
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
|
||||||
// spdlog::warn("Overflow");
|
// flog::warn("Overflow");
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
cnd.notify_all();
|
cnd.notify_all();
|
||||||
|
@ -83,7 +83,7 @@ namespace dsp::multirate {
|
|||||||
int interp = OutSR / gcd;
|
int interp = OutSR / gcd;
|
||||||
int decim = InSR / gcd;
|
int decim = InSR / gcd;
|
||||||
|
|
||||||
spdlog::warn("interp: {0}, decim: {1}", interp, decim);
|
flog::warn("interp: {0}, decim: {1}", interp, decim);
|
||||||
|
|
||||||
// Configure resampler
|
// Configure resampler
|
||||||
double tapSamplerate = _symbolrate * (double)interp;
|
double tapSamplerate = _symbolrate * (double)interp;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <gui/colormaps.h>
|
#include <gui/colormaps.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ namespace colormaps {
|
|||||||
|
|
||||||
void loadMap(std::string path) {
|
void loadMap(std::string path) {
|
||||||
if (!std::filesystem::is_regular_file(path)) {
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
spdlog::error("Could not load {0}, file doesn't exist", path);
|
flog::error("Could not load {0}, file doesn't exist", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ namespace colormaps {
|
|||||||
mapTxt = data["map"].get<std::vector<std::string>>();
|
mapTxt = data["map"].get<std::vector<std::string>>();
|
||||||
}
|
}
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
spdlog::error("Could not load {0}", path);
|
flog::error("Could not load {0}", path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <imgui/stb_image.h>
|
#include <imgui/stb_image.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
|
|
||||||
namespace icons {
|
namespace icons {
|
||||||
ImTextureID LOGO;
|
ImTextureID LOGO;
|
||||||
@ -33,7 +33,7 @@ namespace icons {
|
|||||||
|
|
||||||
bool load(std::string resDir) {
|
bool load(std::string resDir) {
|
||||||
if (!std::filesystem::is_directory(resDir)) {
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
spdlog::error("Invalid resource directory: {0}", resDir);
|
flog::error("Invalid resource directory: {0}", resDir);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,19 +49,19 @@ void MainWindow::init() {
|
|||||||
gui::menu.order.clear();
|
gui::menu.order.clear();
|
||||||
for (auto& elem : menuElements) {
|
for (auto& elem : menuElements) {
|
||||||
if (!elem.contains("name")) {
|
if (!elem.contains("name")) {
|
||||||
spdlog::error("Menu element is missing name key");
|
flog::error("Menu element is missing name key");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!elem["name"].is_string()) {
|
if (!elem["name"].is_string()) {
|
||||||
spdlog::error("Menu element name isn't a string");
|
flog::error("Menu element name isn't a string");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!elem.contains("open")) {
|
if (!elem.contains("open")) {
|
||||||
spdlog::error("Menu element is missing open key");
|
flog::error("Menu element is missing open key");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!elem["open"].is_boolean()) {
|
if (!elem["open"].is_boolean()) {
|
||||||
spdlog::error("Menu element name isn't a string");
|
flog::error("Menu element name isn't a string");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Menu::MenuOption_t opt;
|
Menu::MenuOption_t opt;
|
||||||
@ -95,7 +95,7 @@ void MainWindow::init() {
|
|||||||
vfoCreatedHandler.ctx = this;
|
vfoCreatedHandler.ctx = this;
|
||||||
sigpath::vfoManager.onVfoCreated.bindHandler(&vfoCreatedHandler);
|
sigpath::vfoManager.onVfoCreated.bindHandler(&vfoCreatedHandler);
|
||||||
|
|
||||||
spdlog::info("Loading modules");
|
flog::info("Loading modules");
|
||||||
|
|
||||||
// Load modules from /module directory
|
// Load modules from /module directory
|
||||||
if (std::filesystem::is_directory(modulesDir)) {
|
if (std::filesystem::is_directory(modulesDir)) {
|
||||||
@ -105,13 +105,13 @@ void MainWindow::init() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!file.is_regular_file()) { continue; }
|
if (!file.is_regular_file()) { continue; }
|
||||||
spdlog::info("Loading {0}", path);
|
flog::info("Loading {0}", path);
|
||||||
LoadingScreen::show("Loading " + file.path().filename().string());
|
LoadingScreen::show("Loading " + file.path().filename().string());
|
||||||
core::moduleManager.loadModule(path);
|
core::moduleManager.loadModule(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
flog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read module config
|
// Read module config
|
||||||
@ -124,7 +124,7 @@ void MainWindow::init() {
|
|||||||
for (auto const& path : modules) {
|
for (auto const& path : modules) {
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
std::string apath = std::filesystem::absolute(path).string();
|
std::string apath = std::filesystem::absolute(path).string();
|
||||||
spdlog::info("Loading {0}", apath);
|
flog::info("Loading {0}", apath);
|
||||||
LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string());
|
LoadingScreen::show("Loading " + std::filesystem::path(path).filename().string());
|
||||||
core::moduleManager.loadModule(apath);
|
core::moduleManager.loadModule(apath);
|
||||||
#else
|
#else
|
||||||
@ -136,7 +136,7 @@ void MainWindow::init() {
|
|||||||
for (auto const& [name, _module] : modList) {
|
for (auto const& [name, _module] : modList) {
|
||||||
std::string mod = _module["module"];
|
std::string mod = _module["module"];
|
||||||
bool enabled = _module["enabled"];
|
bool enabled = _module["enabled"];
|
||||||
spdlog::info("Initializing {0} ({1})", name, mod);
|
flog::info("Initializing {0} ({1})", name, mod);
|
||||||
LoadingScreen::show("Initializing " + name + " (" + mod + ")");
|
LoadingScreen::show("Initializing " + name + " (" + mod + ")");
|
||||||
core::moduleManager.createInstance(name, mod);
|
core::moduleManager.createInstance(name, mod);
|
||||||
if (!enabled) { core::moduleManager.disableInstance(name); }
|
if (!enabled) { core::moduleManager.disableInstance(name); }
|
||||||
@ -144,12 +144,12 @@ void MainWindow::init() {
|
|||||||
|
|
||||||
// Load color maps
|
// Load color maps
|
||||||
LoadingScreen::show("Loading color maps");
|
LoadingScreen::show("Loading color maps");
|
||||||
spdlog::info("Loading color maps");
|
flog::info("Loading color maps");
|
||||||
if (std::filesystem::is_directory(resourcesDir + "/colormaps")) {
|
if (std::filesystem::is_directory(resourcesDir + "/colormaps")) {
|
||||||
for (const auto& file : std::filesystem::directory_iterator(resourcesDir + "/colormaps")) {
|
for (const auto& file : std::filesystem::directory_iterator(resourcesDir + "/colormaps")) {
|
||||||
std::string path = file.path().generic_string();
|
std::string path = file.path().generic_string();
|
||||||
LoadingScreen::show("Loading " + file.path().filename().string());
|
LoadingScreen::show("Loading " + file.path().filename().string());
|
||||||
spdlog::info("Loading {0}", path);
|
flog::info("Loading {0}", path);
|
||||||
if (file.path().extension().generic_string() != ".json") {
|
if (file.path().extension().generic_string() != ".json") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ void MainWindow::init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::warn("Color map directory {0} does not exist, not loading modules from directory", modulesDir);
|
flog::warn("Color map directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
gui::waterfall.updatePalletteFromArray(colormaps::maps["Turbo"].map, colormaps::maps["Turbo"].entryCount);
|
gui::waterfall.updatePalletteFromArray(colormaps::maps["Turbo"].map, colormaps::maps["Turbo"].entryCount);
|
||||||
@ -510,7 +510,7 @@ void MainWindow::draw() {
|
|||||||
// ImGui::Text("Buffering: %d", (sigpath::iqFrontEnd.inputBuffer.writeCur - sigpath::iqFrontEnd.inputBuffer.readCur + 32) % 32);
|
// ImGui::Text("Buffering: %d", (sigpath::iqFrontEnd.inputBuffer.writeCur - sigpath::iqFrontEnd.inputBuffer.readCur + 32) % 32);
|
||||||
|
|
||||||
if (ImGui::Button("Test Bug")) {
|
if (ImGui::Button("Test Bug")) {
|
||||||
spdlog::error("Will this make the software crash?");
|
flog::error("Will this make the software crash?");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Button("Testing something")) {
|
if (ImGui::Button("Testing something")) {
|
||||||
|
@ -195,7 +195,7 @@ namespace SmGui {
|
|||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::error("Invalid widget in Drawlist");
|
flog::error("Invalid widget in Drawlist");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem.step != DRAW_STEP_FILL_WIDTH) { nextItemFillWidth = false; }
|
if (elem.step != DRAW_STEP_FILL_WIDTH) { nextItemFillWidth = false; }
|
||||||
@ -293,7 +293,7 @@ namespace SmGui {
|
|||||||
|
|
||||||
// Validate and clear if invalid
|
// Validate and clear if invalid
|
||||||
if (!validate()) {
|
if (!validate()) {
|
||||||
spdlog::error("Drawlist validation failed");
|
flog::error("Drawlist validation failed");
|
||||||
//elements.clear();
|
//elements.clear();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
namespace style {
|
namespace style {
|
||||||
@ -22,7 +22,7 @@ namespace style {
|
|||||||
bool loadFonts(std::string resDir) {
|
bool loadFonts(std::string resDir) {
|
||||||
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
|
ImFontAtlas* fonts = ImGui::GetIO().Fonts;
|
||||||
if (!std::filesystem::is_directory(resDir)) {
|
if (!std::filesystem::is_directory(resDir)) {
|
||||||
spdlog::error("Invalid resource directory: {0}", resDir);
|
flog::error("Invalid resource directory: {0}", resDir);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <json.hpp>
|
#include <json.hpp>
|
||||||
#include <gui/theme_manager.h>
|
#include <gui/theme_manager.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ bool ThemeManager::loadThemesFromDir(std::string path) {
|
|||||||
|
|
||||||
|
|
||||||
if (!std::filesystem::is_directory(path)) {
|
if (!std::filesystem::is_directory(path)) {
|
||||||
spdlog::error("Theme directory doesn't exist: {0}", path);
|
flog::error("Theme directory doesn't exist: {0}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
themes.clear();
|
themes.clear();
|
||||||
@ -39,7 +39,7 @@ bool ThemeManager::loadThemesFromDir(std::string path) {
|
|||||||
|
|
||||||
bool ThemeManager::loadTheme(std::string path) {
|
bool ThemeManager::loadTheme(std::string path) {
|
||||||
if (!std::filesystem::is_regular_file(path)) {
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
spdlog::error("Theme file doesn't exist: {0}", path);
|
flog::error("Theme file doesn't exist: {0}", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,24 +55,24 @@ bool ThemeManager::loadTheme(std::string path) {
|
|||||||
|
|
||||||
// Load theme name
|
// Load theme name
|
||||||
if (!data.contains("name")) {
|
if (!data.contains("name")) {
|
||||||
spdlog::error("Theme {0} is missing the name parameter", path);
|
flog::error("Theme {0} is missing the name parameter", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!data["name"].is_string()) {
|
if (!data["name"].is_string()) {
|
||||||
spdlog::error("Theme {0} contains invalid name field. Expected string", path);
|
flog::error("Theme {0} contains invalid name field. Expected string", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string name = data["name"];
|
std::string name = data["name"];
|
||||||
|
|
||||||
if (themes.find(name) != themes.end()) {
|
if (themes.find(name) != themes.end()) {
|
||||||
spdlog::error("A theme named '{0}' already exists", name);
|
flog::error("A theme named '{0}' already exists", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load theme author if available
|
// Load theme author if available
|
||||||
if (data.contains("author")) {
|
if (data.contains("author")) {
|
||||||
if (!data["author"].is_string()) {
|
if (!data["author"].is_string()) {
|
||||||
spdlog::error("Theme {0} contains invalid author field. Expected string", path);
|
flog::error("Theme {0} contains invalid author field. Expected string", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
thm.author = data["author"];
|
thm.author = data["author"];
|
||||||
@ -86,7 +86,7 @@ bool ThemeManager::loadTheme(std::string path) {
|
|||||||
// Exception for non-imgu colors
|
// Exception for non-imgu colors
|
||||||
if (param == "WaterfallBackground" || param == "ClearColor" || param == "FFTHoldColor") {
|
if (param == "WaterfallBackground" || param == "ClearColor" || param == "FFTHoldColor") {
|
||||||
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
||||||
spdlog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
flog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -97,14 +97,14 @@ bool ThemeManager::loadTheme(std::string path) {
|
|||||||
// If param is a color, check that it's a valid RGBA hex value
|
// If param is a color, check that it's a valid RGBA hex value
|
||||||
if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) {
|
if (IMGUI_COL_IDS.find(param) != IMGUI_COL_IDS.end()) {
|
||||||
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
if (val[0] != '#' || !std::all_of(val.begin() + 1, val.end(), ::isxdigit) || val.length() != 9) {
|
||||||
spdlog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
flog::error("Theme {0} contains invalid {1} field. Expected hex RGBA color", path, param);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isValid = true;
|
isValid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
spdlog::error("Theme {0} contains unknown {1} field.", path, param);
|
flog::error("Theme {0} contains unknown {1} field.", path, param);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ bool ThemeManager::loadTheme(std::string path) {
|
|||||||
|
|
||||||
bool ThemeManager::applyTheme(std::string name) {
|
bool ThemeManager::applyTheme(std::string name) {
|
||||||
if (themes.find(name) == themes.end()) {
|
if (themes.find(name) == themes.end()) {
|
||||||
spdlog::error("Unknown theme: {0}", name);
|
flog::error("Unknown theme: {0}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <gui/widgets/bandplan.h>
|
#include <gui/widgets/bandplan.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@ -56,7 +56,7 @@ namespace bandplan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void to_json(json& j, const BandPlanColor_t& ct) {
|
void to_json(json& j, const BandPlanColor_t& ct) {
|
||||||
spdlog::error("ImGui color to JSON not implemented!!!");
|
flog::error("ImGui color to JSON not implemented!!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(const json& j, BandPlanColor_t& ct) {
|
void from_json(const json& j, BandPlanColor_t& ct) {
|
||||||
@ -81,7 +81,7 @@ namespace bandplan {
|
|||||||
|
|
||||||
BandPlan_t plan = data.get<BandPlan_t>();
|
BandPlan_t plan = data.get<BandPlan_t>();
|
||||||
if (bandplans.find(plan.name) != bandplans.end()) {
|
if (bandplans.find(plan.name) != bandplans.end()) {
|
||||||
spdlog::error("Duplicate band plan name ({0}), not loading.", plan.name);
|
flog::error("Duplicate band plan name ({0}), not loading.", plan.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bandplans[plan.name] = plan;
|
bandplans[plan.name] = plan;
|
||||||
@ -91,11 +91,11 @@ namespace bandplan {
|
|||||||
|
|
||||||
void loadFromDir(std::string path) {
|
void loadFromDir(std::string path) {
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
spdlog::error("Band Plan directory does not exist");
|
flog::error("Band Plan directory does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!std::filesystem::is_directory(path)) {
|
if (!std::filesystem::is_directory(path)) {
|
||||||
spdlog::error("Band Plan directory isn't a directory...");
|
flog::error("Band Plan directory isn't a directory...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bandplans.clear();
|
bandplans.clear();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include <imutils.h>
|
#include <imutils.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
|
|
||||||
@ -1292,7 +1292,7 @@ namespace ImGui {
|
|||||||
void WaterFall::showWaterfall() {
|
void WaterFall::showWaterfall() {
|
||||||
buf_mtx.lock();
|
buf_mtx.lock();
|
||||||
if (rawFFTs == NULL) {
|
if (rawFFTs == NULL) {
|
||||||
spdlog::error("Null rawFFT");
|
flog::error("Null rawFFT");
|
||||||
}
|
}
|
||||||
waterfallVisible = true;
|
waterfallVisible = true;
|
||||||
onResize();
|
onResize();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <module.h>
|
#include <module.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
|
|
||||||
ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
||||||
Module_t mod;
|
Module_t mod;
|
||||||
@ -8,12 +8,12 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
|||||||
// On android, the path has to be relative, don't make it absolute
|
// On android, the path has to be relative, don't make it absolute
|
||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
spdlog::error("{0} does not exist", path);
|
flog::error("{0} does not exist", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (!std::filesystem::is_regular_file(path)) {
|
if (!std::filesystem::is_regular_file(path)) {
|
||||||
spdlog::error("{0} isn't a loadable module", path);
|
flog::error("{0} isn't a loadable module", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
mod.handle = LoadLibraryA(path.c_str());
|
mod.handle = LoadLibraryA(path.c_str());
|
||||||
if (mod.handle == NULL) {
|
if (mod.handle == NULL) {
|
||||||
spdlog::error("Couldn't load {0}. Error code: {1}", path, GetLastError());
|
flog::error("Couldn't load {0}. Error code: {1}", path, (int)GetLastError());
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
|||||||
#else
|
#else
|
||||||
mod.handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
mod.handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (mod.handle == NULL) {
|
if (mod.handle == NULL) {
|
||||||
spdlog::error("Couldn't load {0}.", path);
|
flog::error("Couldn't load {0}.", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -44,32 +44,32 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
|||||||
mod.end = (void (*)())dlsym(mod.handle, "_END_");
|
mod.end = (void (*)())dlsym(mod.handle, "_END_");
|
||||||
#endif
|
#endif
|
||||||
if (mod.info == NULL) {
|
if (mod.info == NULL) {
|
||||||
spdlog::error("{0} is missing _INFO_ symbol", path);
|
flog::error("{0} is missing _INFO_ symbol", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (mod.init == NULL) {
|
if (mod.init == NULL) {
|
||||||
spdlog::error("{0} is missing _INIT_ symbol", path);
|
flog::error("{0} is missing _INIT_ symbol", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (mod.createInstance == NULL) {
|
if (mod.createInstance == NULL) {
|
||||||
spdlog::error("{0} is missing _CREATE_INSTANCE_ symbol", path);
|
flog::error("{0} is missing _CREATE_INSTANCE_ symbol", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (mod.deleteInstance == NULL) {
|
if (mod.deleteInstance == NULL) {
|
||||||
spdlog::error("{0} is missing _DELETE_INSTANCE_ symbol", path);
|
flog::error("{0} is missing _DELETE_INSTANCE_ symbol", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (mod.end == NULL) {
|
if (mod.end == NULL) {
|
||||||
spdlog::error("{0} is missing _END_ symbol", path);
|
flog::error("{0} is missing _END_ symbol", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
if (modules.find(mod.info->name) != modules.end()) {
|
if (modules.find(mod.info->name) != modules.end()) {
|
||||||
spdlog::error("{0} has the same name as an already loaded module", path);
|
flog::error("{0} has the same name as an already loaded module", path);
|
||||||
mod.handle = NULL;
|
mod.handle = NULL;
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
@ -85,16 +85,16 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
|||||||
|
|
||||||
int ModuleManager::createInstance(std::string name, std::string module) {
|
int ModuleManager::createInstance(std::string name, std::string module) {
|
||||||
if (modules.find(module) == modules.end()) {
|
if (modules.find(module) == modules.end()) {
|
||||||
spdlog::error("Module '{0}' doesn't exist", module);
|
flog::error("Module '{0}' doesn't exist", module);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (instances.find(name) != instances.end()) {
|
if (instances.find(name) != instances.end()) {
|
||||||
spdlog::error("A module instance with the name '{0}' already exists", name);
|
flog::error("A module instance with the name '{0}' already exists", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int maxCount = modules[module].info->maxInstances;
|
int maxCount = modules[module].info->maxInstances;
|
||||||
if (countModuleInstances(module) >= maxCount && maxCount > 0) {
|
if (countModuleInstances(module) >= maxCount && maxCount > 0) {
|
||||||
spdlog::error("Maximum number of instances reached for '{0}'", module);
|
flog::error("Maximum number of instances reached for '{0}'", module);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Instance_t inst;
|
Instance_t inst;
|
||||||
@ -107,7 +107,7 @@ int ModuleManager::createInstance(std::string name, std::string module) {
|
|||||||
|
|
||||||
int ModuleManager::deleteInstance(std::string name) {
|
int ModuleManager::deleteInstance(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Tried to remove non-existent instance '{0}'", name);
|
flog::error("Tried to remove non-existent instance '{0}'", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
onInstanceDelete.emit(name);
|
onInstanceDelete.emit(name);
|
||||||
@ -119,13 +119,13 @@ int ModuleManager::deleteInstance(std::string name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ModuleManager::deleteInstance(ModuleManager::Instance* instance) {
|
int ModuleManager::deleteInstance(ModuleManager::Instance* instance) {
|
||||||
spdlog::error("Delete instance not implemented");
|
flog::error("Delete instance not implemented");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ModuleManager::enableInstance(std::string name) {
|
int ModuleManager::enableInstance(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Cannot enable '{0}', instance doesn't exist", name);
|
flog::error("Cannot enable '{0}', instance doesn't exist", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
instances[name].instance->enable();
|
instances[name].instance->enable();
|
||||||
@ -134,7 +134,7 @@ int ModuleManager::enableInstance(std::string name) {
|
|||||||
|
|
||||||
int ModuleManager::disableInstance(std::string name) {
|
int ModuleManager::disableInstance(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Cannot disable '{0}', instance doesn't exist", name);
|
flog::error("Cannot disable '{0}', instance doesn't exist", name);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
instances[name].instance->disable();
|
instances[name].instance->disable();
|
||||||
@ -143,7 +143,7 @@ int ModuleManager::disableInstance(std::string name) {
|
|||||||
|
|
||||||
bool ModuleManager::instanceEnabled(std::string name) {
|
bool ModuleManager::instanceEnabled(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Cannot check if '{0}' is enabled, instance doesn't exist", name);
|
flog::error("Cannot check if '{0}' is enabled, instance doesn't exist", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return instances[name].instance->isEnabled();
|
return instances[name].instance->isEnabled();
|
||||||
@ -151,7 +151,7 @@ bool ModuleManager::instanceEnabled(std::string name) {
|
|||||||
|
|
||||||
void ModuleManager::postInit(std::string name) {
|
void ModuleManager::postInit(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Cannot post-init '{0}', instance doesn't exist", name);
|
flog::error("Cannot post-init '{0}', instance doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instances[name].instance->postInit();
|
instances[name].instance->postInit();
|
||||||
@ -159,7 +159,7 @@ void ModuleManager::postInit(std::string name) {
|
|||||||
|
|
||||||
std::string ModuleManager::getInstanceModuleName(std::string name) {
|
std::string ModuleManager::getInstanceModuleName(std::string name) {
|
||||||
if (instances.find(name) == instances.end()) {
|
if (instances.find(name) == instances.end()) {
|
||||||
spdlog::error("Cannot get module name of'{0}', instance doesn't exist", name);
|
flog::error("Cannot get module name of'{0}', instance doesn't exist", name);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return std::string(instances[name].module.info->name);
|
return std::string(instances[name].module.info->name);
|
||||||
@ -167,7 +167,7 @@ std::string ModuleManager::getInstanceModuleName(std::string name) {
|
|||||||
|
|
||||||
int ModuleManager::countModuleInstances(std::string module) {
|
int ModuleManager::countModuleInstances(std::string module) {
|
||||||
if (modules.find(module) == modules.end()) {
|
if (modules.find(module) == modules.end()) {
|
||||||
spdlog::error("Cannot count instances of '{0}', Module doesn't exist", module);
|
flog::error("Cannot count instances of '{0}', Module doesn't exist", module);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ModuleManager::Module_t mod = modules[module];
|
ModuleManager::Module_t mod = modules[module];
|
||||||
@ -180,7 +180,7 @@ int ModuleManager::countModuleInstances(std::string module) {
|
|||||||
|
|
||||||
void ModuleManager::doPostInitAll() {
|
void ModuleManager::doPostInitAll() {
|
||||||
for (auto& [name, inst] : instances) {
|
for (auto& [name, inst] : instances) {
|
||||||
spdlog::info("Running post-init for {0}", name);
|
flog::info("Running post-init for {0}", name);
|
||||||
inst.instance->postInit();
|
inst.instance->postInit();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,7 @@
|
|||||||
bool ModuleComManager::registerInterface(std::string moduleName, std::string name, void (*handler)(int code, void* in, void* out, void* ctx), void* ctx) {
|
bool ModuleComManager::registerInterface(std::string moduleName, std::string name, void (*handler)(int code, void* in, void* out, void* ctx), void* ctx) {
|
||||||
std::lock_guard<std::mutex> lck(mtx);
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
if (interfaces.find(name) != interfaces.end()) {
|
if (interfaces.find(name) != interfaces.end()) {
|
||||||
spdlog::error("Tried creating module interface with an existing name: {0}", name);
|
flog::error("Tried creating module interface with an existing name: {0}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ModuleComInterface iface;
|
ModuleComInterface iface;
|
||||||
@ -17,7 +17,7 @@ bool ModuleComManager::registerInterface(std::string moduleName, std::string nam
|
|||||||
bool ModuleComManager::unregisterInterface(std::string name) {
|
bool ModuleComManager::unregisterInterface(std::string name) {
|
||||||
std::lock_guard<std::mutex> lck(mtx);
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
if (interfaces.find(name) == interfaces.end()) {
|
if (interfaces.find(name) == interfaces.end()) {
|
||||||
spdlog::error("Tried to erase module interface with unknown name: {0}", name);
|
flog::error("Tried to erase module interface with unknown name: {0}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
interfaces.erase(name);
|
interfaces.erase(name);
|
||||||
@ -33,7 +33,7 @@ bool ModuleComManager::interfaceExists(std::string name) {
|
|||||||
std::string ModuleComManager::getModuleName(std::string name) {
|
std::string ModuleComManager::getModuleName(std::string name) {
|
||||||
std::lock_guard<std::mutex> lck(mtx);
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
if (interfaces.find(name) == interfaces.end()) {
|
if (interfaces.find(name) == interfaces.end()) {
|
||||||
spdlog::error("Tried to call unknown module interface: {0}", name);
|
flog::error("Tried to call unknown module interface: {0}", name);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return interfaces[name].moduleName;
|
return interfaces[name].moduleName;
|
||||||
@ -42,7 +42,7 @@ std::string ModuleComManager::getModuleName(std::string name) {
|
|||||||
bool ModuleComManager::callInterface(std::string name, int code, void* in, void* out) {
|
bool ModuleComManager::callInterface(std::string name, int code, void* in, void* out) {
|
||||||
std::lock_guard<std::mutex> lck(mtx);
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
if (interfaces.find(name) == interfaces.end()) {
|
if (interfaces.find(name) == interfaces.end()) {
|
||||||
spdlog::error("Tried to call unknown module interface: {0}", name);
|
flog::error("Tried to call unknown module interface: {0}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ModuleComInterface iface = interfaces[name];
|
ModuleComInterface iface = interfaces[name];
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
|
|
||||||
struct ModuleComInterface {
|
struct ModuleComInterface {
|
||||||
std::string moduleName;
|
std::string moduleName;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@ -47,7 +47,7 @@ namespace server {
|
|||||||
double sampleRate = 1000000.0;
|
double sampleRate = 1000000.0;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
spdlog::info("=====| SERVER MODE |=====");
|
flog::info("=====| SERVER MODE |=====");
|
||||||
|
|
||||||
// Init DSP
|
// Init DSP
|
||||||
comp.init(&dummyInput, dsp::compression::PCM_TYPE_I8);
|
comp.init(&dummyInput, dsp::compression::PCM_TYPE_I8);
|
||||||
@ -87,7 +87,7 @@ namespace server {
|
|||||||
// Initialize SmGui in server mode
|
// Initialize SmGui in server mode
|
||||||
SmGui::init(true);
|
SmGui::init(true);
|
||||||
|
|
||||||
spdlog::info("Loading modules");
|
flog::info("Loading modules");
|
||||||
// Load modules and check type to only load sources ( TODO: Have a proper type parameter int the info )
|
// Load modules and check type to only load sources ( TODO: Have a proper type parameter int the info )
|
||||||
// TODO LATER: Add whitelist/blacklist stuff
|
// TODO LATER: Add whitelist/blacklist stuff
|
||||||
if (std::filesystem::is_directory(modulesDir)) {
|
if (std::filesystem::is_directory(modulesDir)) {
|
||||||
@ -100,12 +100,12 @@ namespace server {
|
|||||||
if (!file.is_regular_file()) { continue; }
|
if (!file.is_regular_file()) { continue; }
|
||||||
if (fn.find("source") == std::string::npos) { continue; }
|
if (fn.find("source") == std::string::npos) { continue; }
|
||||||
|
|
||||||
spdlog::info("Loading {0}", path);
|
flog::info("Loading {0}", path);
|
||||||
core::moduleManager.loadModule(path);
|
core::moduleManager.loadModule(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
flog::warn("Module directory {0} does not exist, not loading modules from directory", modulesDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load additional modules through the config ( TODO: Have a proper type parameter int the info )
|
// Load additional modules through the config ( TODO: Have a proper type parameter int the info )
|
||||||
@ -120,7 +120,7 @@ namespace server {
|
|||||||
if (!std::filesystem::is_regular_file(file)) { continue; }
|
if (!std::filesystem::is_regular_file(file)) { continue; }
|
||||||
if (fn.find("source") == std::string::npos) { continue; }
|
if (fn.find("source") == std::string::npos) { continue; }
|
||||||
|
|
||||||
spdlog::info("Loading {0}", path);
|
flog::info("Loading {0}", path);
|
||||||
core::moduleManager.loadModule(path);
|
core::moduleManager.loadModule(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ namespace server {
|
|||||||
std::string mod = _module["module"];
|
std::string mod = _module["module"];
|
||||||
bool enabled = _module["enabled"];
|
bool enabled = _module["enabled"];
|
||||||
if (core::moduleManager.modules.find(mod) == core::moduleManager.modules.end()) { continue; }
|
if (core::moduleManager.modules.find(mod) == core::moduleManager.modules.end()) { continue; }
|
||||||
spdlog::info("Initializing {0} ({1})", name, mod);
|
flog::info("Initializing {0} ({1})", name, mod);
|
||||||
core::moduleManager.createInstance(name, mod);
|
core::moduleManager.createInstance(name, mod);
|
||||||
if (!enabled) { core::moduleManager.disableInstance(name); }
|
if (!enabled) { core::moduleManager.disableInstance(name); }
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ namespace server {
|
|||||||
listener = net::listen(host, port);
|
listener = net::listen(host, port);
|
||||||
listener->acceptAsync(_clientHandler, NULL);
|
listener->acceptAsync(_clientHandler, NULL);
|
||||||
|
|
||||||
spdlog::info("Ready, listening on {0}:{1}", host, port);
|
flog::info("Ready, listening on {0}:{1}", host, port);
|
||||||
while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
|
while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); }
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -163,7 +163,7 @@ namespace server {
|
|||||||
void _clientHandler(net::Conn conn, void* ctx) {
|
void _clientHandler(net::Conn conn, void* ctx) {
|
||||||
// Reject if someone else is already connected
|
// Reject if someone else is already connected
|
||||||
if (client && client->isOpen()) {
|
if (client && client->isOpen()) {
|
||||||
spdlog::info("REJECTED Connection from {0}:{1}, another client is already connected.", "TODO", "TODO");
|
flog::info("REJECTED Connection from {0}:{1}, another client is already connected.", "TODO", "TODO");
|
||||||
|
|
||||||
// Issue a disconnect command to the client
|
// Issue a disconnect command to the client
|
||||||
uint8_t buf[sizeof(PacketHeader) + sizeof(CommandHeader)];
|
uint8_t buf[sizeof(PacketHeader) + sizeof(CommandHeader)];
|
||||||
@ -184,7 +184,7 @@ namespace server {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::info("Connection from {0}:{1}", "TODO", "TODO");
|
flog::info("Connection from {0}:{1}", "TODO", "TODO");
|
||||||
client = std::move(conn);
|
client = std::move(conn);
|
||||||
client->readAsync(sizeof(PacketHeader), rbuf, _packetHandler, NULL);
|
client->readAsync(sizeof(PacketHeader), rbuf, _packetHandler, NULL);
|
||||||
|
|
||||||
@ -299,7 +299,7 @@ namespace server {
|
|||||||
compression = *(uint8_t*)data;
|
compression = *(uint8_t*)data;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::error("Invalid Command: {0} (len = {1})", cmd, len);
|
flog::error("Invalid Command: {0} (len = {1})", (int)cmd, len);
|
||||||
sendError(ERROR_INVALID_COMMAND);
|
sendError(ERROR_INVALID_COMMAND);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include "iq_frontend.h"
|
#include "iq_frontend.h"
|
||||||
#include "../dsp/window/blackman.h"
|
#include "../dsp/window/blackman.h"
|
||||||
#include "../dsp/window/nuttall.h"
|
#include "../dsp/window/nuttall.h"
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ void IQFrontEnd::unbindIQStream(dsp::stream<dsp::complex_t>* stream) {
|
|||||||
dsp::channel::RxVFO* IQFrontEnd::addVFO(std::string name, double sampleRate, double bandwidth, double offset) {
|
dsp::channel::RxVFO* IQFrontEnd::addVFO(std::string name, double sampleRate, double bandwidth, double offset) {
|
||||||
// Make sure no other VFO with that name already exists
|
// Make sure no other VFO with that name already exists
|
||||||
if (vfos.find(name) != vfos.end()) {
|
if (vfos.find(name) != vfos.end()) {
|
||||||
spdlog::error("[IQFrontEnd] Tried to add VFO with existing name.");
|
flog::error("[IQFrontEnd] Tried to add VFO with existing name.");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +162,7 @@ dsp::channel::RxVFO* IQFrontEnd::addVFO(std::string name, double sampleRate, dou
|
|||||||
void IQFrontEnd::removeVFO(std::string name) {
|
void IQFrontEnd::removeVFO(std::string name) {
|
||||||
// Make sure that a VFO with that name exists
|
// Make sure that a VFO with that name exists
|
||||||
if (vfos.find(name) == vfos.end()) {
|
if (vfos.find(name) == vfos.end()) {
|
||||||
spdlog::error("[IQFrontEnd] Tried to remove a VFO that doesn't exist.");
|
flog::error("[IQFrontEnd] Tried to remove a VFO that doesn't exist.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include <signal_path/sink.h>
|
#include <signal_path/sink.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <gui/icons.h>
|
#include <gui/icons.h>
|
||||||
@ -87,7 +87,7 @@ void SinkManager::Stream::setSampleRate(float sampleRate) {
|
|||||||
|
|
||||||
void SinkManager::registerSinkProvider(std::string name, SinkProvider provider) {
|
void SinkManager::registerSinkProvider(std::string name, SinkProvider provider) {
|
||||||
if (providers.find(name) != providers.end()) {
|
if (providers.find(name) != providers.end()) {
|
||||||
spdlog::error("Cannot register sink provider '{0}', this name is already taken", name);
|
flog::error("Cannot register sink provider '{0}', this name is already taken", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ void SinkManager::registerSinkProvider(std::string name, SinkProvider provider)
|
|||||||
|
|
||||||
void SinkManager::unregisterSinkProvider(std::string name) {
|
void SinkManager::unregisterSinkProvider(std::string name) {
|
||||||
if (providers.find(name) == providers.end()) {
|
if (providers.find(name) == providers.end()) {
|
||||||
spdlog::error("Cannot unregister sink provider '{0}', no such provider exists.", name);
|
flog::error("Cannot unregister sink provider '{0}', no such provider exists.", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +137,7 @@ void SinkManager::unregisterSinkProvider(std::string name) {
|
|||||||
|
|
||||||
void SinkManager::registerStream(std::string name, SinkManager::Stream* stream) {
|
void SinkManager::registerStream(std::string name, SinkManager::Stream* stream) {
|
||||||
if (streams.find(name) != streams.end()) {
|
if (streams.find(name) != streams.end()) {
|
||||||
spdlog::error("Cannot register stream '{0}', this name is already taken", name);
|
flog::error("Cannot register stream '{0}', this name is already taken", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ void SinkManager::registerStream(std::string name, SinkManager::Stream* stream)
|
|||||||
|
|
||||||
void SinkManager::unregisterStream(std::string name) {
|
void SinkManager::unregisterStream(std::string name) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot unregister stream '{0}', this stream doesn't exist", name);
|
flog::error("Cannot unregister stream '{0}', this stream doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onStreamUnregister.emit(name);
|
onStreamUnregister.emit(name);
|
||||||
@ -177,7 +177,7 @@ void SinkManager::unregisterStream(std::string name) {
|
|||||||
|
|
||||||
void SinkManager::startStream(std::string name) {
|
void SinkManager::startStream(std::string name) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot start stream '{0}', this stream doesn't exist", name);
|
flog::error("Cannot start stream '{0}', this stream doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
streams[name]->start();
|
streams[name]->start();
|
||||||
@ -185,7 +185,7 @@ void SinkManager::startStream(std::string name) {
|
|||||||
|
|
||||||
void SinkManager::stopStream(std::string name) {
|
void SinkManager::stopStream(std::string name) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot stop stream '{0}', this stream doesn't exist", name);
|
flog::error("Cannot stop stream '{0}', this stream doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
streams[name]->stop();
|
streams[name]->stop();
|
||||||
@ -193,7 +193,7 @@ void SinkManager::stopStream(std::string name) {
|
|||||||
|
|
||||||
float SinkManager::getStreamSampleRate(std::string name) {
|
float SinkManager::getStreamSampleRate(std::string name) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot get sample rate of stream '{0}', this stream doesn't exist", name);
|
flog::error("Cannot get sample rate of stream '{0}', this stream doesn't exist", name);
|
||||||
return -1.0f;
|
return -1.0f;
|
||||||
}
|
}
|
||||||
return streams[name]->getSampleRate();
|
return streams[name]->getSampleRate();
|
||||||
@ -201,7 +201,7 @@ float SinkManager::getStreamSampleRate(std::string name) {
|
|||||||
|
|
||||||
dsp::stream<dsp::stereo_t>* SinkManager::bindStream(std::string name) {
|
dsp::stream<dsp::stereo_t>* SinkManager::bindStream(std::string name) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot bind to stream '{0}'. Stream doesn't exist", name);
|
flog::error("Cannot bind to stream '{0}'. Stream doesn't exist", name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return streams[name]->bindStream();
|
return streams[name]->bindStream();
|
||||||
@ -209,7 +209,7 @@ dsp::stream<dsp::stereo_t>* SinkManager::bindStream(std::string name) {
|
|||||||
|
|
||||||
void SinkManager::unbindStream(std::string name, dsp::stream<dsp::stereo_t>* stream) {
|
void SinkManager::unbindStream(std::string name, dsp::stream<dsp::stereo_t>* stream) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot unbind from stream '{0}'. Stream doesn't exist", name);
|
flog::error("Cannot unbind from stream '{0}'. Stream doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
streams[name]->unbindStream(stream);
|
streams[name]->unbindStream(stream);
|
||||||
@ -217,12 +217,12 @@ void SinkManager::unbindStream(std::string name, dsp::stream<dsp::stereo_t>* str
|
|||||||
|
|
||||||
void SinkManager::setStreamSink(std::string name, std::string providerName) {
|
void SinkManager::setStreamSink(std::string name, std::string providerName) {
|
||||||
if (streams.find(name) == streams.end()) {
|
if (streams.find(name) == streams.end()) {
|
||||||
spdlog::error("Cannot set sink for stream '{0}'. Stream doesn't exist", name);
|
flog::error("Cannot set sink for stream '{0}'. Stream doesn't exist", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Stream* stream = streams[name];
|
Stream* stream = streams[name];
|
||||||
if (providers.find(providerName) == providers.end()) {
|
if (providers.find(providerName) == providers.end()) {
|
||||||
spdlog::error("Unknown sink provider '{0}'", providerName);
|
flog::error("Unknown sink provider '{0}'", providerName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include <server.h>
|
#include <server.h>
|
||||||
#include <signal_path/source.h>
|
#include <signal_path/source.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <utils/flog.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ SourceManager::SourceManager() {
|
|||||||
|
|
||||||
void SourceManager::registerSource(std::string name, SourceHandler* handler) {
|
void SourceManager::registerSource(std::string name, SourceHandler* handler) {
|
||||||
if (sources.find(name) != sources.end()) {
|
if (sources.find(name) != sources.end()) {
|
||||||
spdlog::error("Tried to register new source with existing name: {0}", name);
|
flog::error("Tried to register new source with existing name: {0}", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sources[name] = handler;
|
sources[name] = handler;
|
||||||
@ -18,7 +18,7 @@ void SourceManager::registerSource(std::string name, SourceHandler* handler) {
|
|||||||
|
|
||||||
void SourceManager::unregisterSource(std::string name) {
|
void SourceManager::unregisterSource(std::string name) {
|
||||||
if (sources.find(name) == sources.end()) {
|
if (sources.find(name) == sources.end()) {
|
||||||
spdlog::error("Tried to unregister non existent source: {0}", name);
|
flog::error("Tried to unregister non existent source: {0}", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSourceUnregister.emit(name);
|
onSourceUnregister.emit(name);
|
||||||
@ -41,7 +41,7 @@ std::vector<std::string> SourceManager::getSourceNames() {
|
|||||||
|
|
||||||
void SourceManager::selectSource(std::string name) {
|
void SourceManager::selectSource(std::string name) {
|
||||||
if (sources.find(name) == sources.end()) {
|
if (sources.find(name) == sources.end()) {
|
||||||
spdlog::error("Tried to select non existent source: {0}", name);
|
flog::error("Tried to select non existent source: {0}", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedHandler != NULL) {
|
if (selectedHandler != NULL) {
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Async logging using global thread pool
|
|
||||||
// All loggers created here share same global thread pool.
|
|
||||||
// Each log message is pushed to a queue along with a shared pointer to the
|
|
||||||
// logger.
|
|
||||||
// If a logger deleted while having pending messages in the queue, it's actual
|
|
||||||
// destruction will defer
|
|
||||||
// until all its messages are processed by the thread pool.
|
|
||||||
// This is because each message in the queue holds a shared_ptr to the
|
|
||||||
// originating logger.
|
|
||||||
|
|
||||||
#include <spdlog/async_logger.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
#include <spdlog/details/thread_pool.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
static const size_t default_async_q_size = 8192;
|
|
||||||
}
|
|
||||||
|
|
||||||
// async logger factory - creates async loggers backed with thread pool.
|
|
||||||
// if a global thread pool doesn't already exist, create it with default queue
|
|
||||||
// size of 8192 items and single thread.
|
|
||||||
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|
||||||
struct async_factory_impl
|
|
||||||
{
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
|
||||||
{
|
|
||||||
auto ®istry_inst = details::registry::instance();
|
|
||||||
|
|
||||||
// create global thread pool if not already exists..
|
|
||||||
|
|
||||||
auto &mutex = registry_inst.tp_mutex();
|
|
||||||
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
|
||||||
auto tp = registry_inst.get_tp();
|
|
||||||
if (tp == nullptr)
|
|
||||||
{
|
|
||||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
|
|
||||||
registry_inst.set_tp(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
|
||||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
|
||||||
registry_inst.initialize_logger(new_logger);
|
|
||||||
return new_logger;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|
||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
|
||||||
{
|
|
||||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
|
||||||
{
|
|
||||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set global thread pool.
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
|
||||||
{
|
|
||||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
|
|
||||||
details::registry::instance().set_tp(std::move(tp));
|
|
||||||
}
|
|
||||||
|
|
||||||
// set global thread pool.
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
|
||||||
{
|
|
||||||
init_thread_pool(q_size, thread_count, [] {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the global thread pool.
|
|
||||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
|
||||||
{
|
|
||||||
return details::registry::instance().get_tp();
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,92 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/async_logger.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#include <spdlog/details/thread_pool.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// send the log message to the thread pool
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send flush request to the thread pool
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::flush_()
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// backend functions - called from the thread pool to do the actual job
|
|
||||||
//
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_flush_(msg))
|
|
||||||
{
|
|
||||||
backend_flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
|
||||||
{
|
|
||||||
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
|
||||||
cloned->name_ = std::move(new_name);
|
|
||||||
return cloned;
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Fast asynchronous logger.
|
|
||||||
// Uses pre allocated queue.
|
|
||||||
// Creates a single back thread to pop messages from the queue and log them.
|
|
||||||
//
|
|
||||||
// Upon each log write the logger:
|
|
||||||
// 1. Checks if its log level is enough to log the message
|
|
||||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
|
||||||
// space is available in the queue)
|
|
||||||
// Upon destruction, logs all remaining messages in the queue before
|
|
||||||
// destructing..
|
|
||||||
|
|
||||||
#include <spdlog/logger.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
|
||||||
enum class async_overflow_policy
|
|
||||||
{
|
|
||||||
block, // Block until message can be enqueued
|
|
||||||
overrun_oldest // Discard oldest message in the queue if full when trying to
|
|
||||||
// add new item.
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
class thread_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
|
||||||
{
|
|
||||||
friend class details::thread_pool;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename It>
|
|
||||||
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
|
||||||
: logger(std::move(logger_name), begin, end)
|
|
||||||
, thread_pool_(std::move(tp))
|
|
||||||
, overflow_policy_(overflow_policy)
|
|
||||||
{}
|
|
||||||
|
|
||||||
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
|
||||||
void backend_flush_();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
|
||||||
async_overflow_policy overflow_policy_;
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "async_logger-inl.h"
|
|
||||||
#endif
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
|
||||||
//
|
|
||||||
// set all loggers to debug level:
|
|
||||||
// example.exe "SPDLOG_LEVEL=debug"
|
|
||||||
|
|
||||||
// set logger1 to trace level
|
|
||||||
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
|
||||||
|
|
||||||
// turn off all logging except for logger1 and logger2:
|
|
||||||
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
|
|
||||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
|
||||||
void load_argv_levels(int argc, const char **argv)
|
|
||||||
{
|
|
||||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
|
||||||
for (int i = 1; i < argc; i++)
|
|
||||||
{
|
|
||||||
std::string arg = argv[i];
|
|
||||||
if (arg.find(spdlog_level_prefix) == 0)
|
|
||||||
{
|
|
||||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
|
||||||
auto levels = helpers::extract_levels(levels_string);
|
|
||||||
details::registry::instance().update_levels(std::move(levels));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_argv_levels(int argc, char **argv)
|
|
||||||
{
|
|
||||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Init levels and patterns from env variables SPDLOG_LEVEL
|
|
||||||
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
|
||||||
// Note - fallback to "info" level on unrecognized levels
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// set global level to debug:
|
|
||||||
// export SPDLOG_LEVEL=debug
|
|
||||||
//
|
|
||||||
// turn off all logging except for logger1:
|
|
||||||
// export SPDLOG_LEVEL="off,logger1=debug"
|
|
||||||
//
|
|
||||||
|
|
||||||
// turn off all logging except for logger1 and logger2:
|
|
||||||
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
void load_env_levels()
|
|
||||||
{
|
|
||||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
|
||||||
auto levels = helpers::extract_levels(env_val);
|
|
||||||
details::registry::instance().update_levels(std::move(levels));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@ -1,103 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
namespace helpers {
|
|
||||||
|
|
||||||
// inplace convert to lowercase
|
|
||||||
inline std::string &to_lower_(std::string &str)
|
|
||||||
{
|
|
||||||
std::transform(
|
|
||||||
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inplace trim spaces
|
|
||||||
inline std::string &trim_(std::string &str)
|
|
||||||
{
|
|
||||||
const char *spaces = " \n\r\t";
|
|
||||||
str.erase(str.find_last_not_of(spaces) + 1);
|
|
||||||
str.erase(0, str.find_first_not_of(spaces));
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return (name,value) trimmed pair from given "name=value" string.
|
|
||||||
// return empty string on missing parts
|
|
||||||
// "key=val" => ("key", "val")
|
|
||||||
// " key = val " => ("key", "val")
|
|
||||||
// "key=" => ("key", "")
|
|
||||||
// "val" => ("", "val")
|
|
||||||
|
|
||||||
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
|
|
||||||
{
|
|
||||||
auto n = str.find(sep);
|
|
||||||
std::string k, v;
|
|
||||||
if (n == std::string::npos)
|
|
||||||
{
|
|
||||||
v = str;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
k = str.substr(0, n);
|
|
||||||
v = str.substr(n + 1);
|
|
||||||
}
|
|
||||||
return std::make_pair(trim_(k), trim_(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
|
||||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
|
||||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
|
|
||||||
{
|
|
||||||
std::string token;
|
|
||||||
std::istringstream token_stream(str);
|
|
||||||
std::unordered_map<std::string, std::string> rv{};
|
|
||||||
while (std::getline(token_stream, token, ','))
|
|
||||||
{
|
|
||||||
if (token.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto kv = extract_kv_('=', token);
|
|
||||||
rv[kv.first] = kv.second;
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
|
|
||||||
{
|
|
||||||
auto key_vals = extract_key_vals_(input);
|
|
||||||
log_levels rv;
|
|
||||||
|
|
||||||
for (auto &name_level : key_vals)
|
|
||||||
{
|
|
||||||
auto &logger_name = name_level.first;
|
|
||||||
auto level_name = to_lower_(name_level.second);
|
|
||||||
auto level = level::from_str(level_name);
|
|
||||||
// fallback to "info" if unrecognized level name
|
|
||||||
if (level == level::off && level_name != "off")
|
|
||||||
{
|
|
||||||
level = level::info;
|
|
||||||
}
|
|
||||||
rv.set(logger_name, level);
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace helpers
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/cfg/log_levels.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
namespace helpers {
|
|
||||||
//
|
|
||||||
// Init levels from given string
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// set global level to debug: "debug"
|
|
||||||
// turn off all logging except for logger1: "off,logger1=debug"
|
|
||||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
|
||||||
//
|
|
||||||
SPDLOG_API log_levels extract_levels(const std::string &txt);
|
|
||||||
} // namespace helpers
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "helpers-inl.h"
|
|
||||||
#endif // SPDLOG_HEADER_ONLY
|
|
@ -1,47 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
class log_levels
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
|
|
||||||
spdlog::level::level_enum default_level_ = level::info;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void set(const std::string &logger_name, level::level_enum lvl)
|
|
||||||
{
|
|
||||||
if (logger_name.empty())
|
|
||||||
{
|
|
||||||
default_level_ = lvl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
levels_[logger_name] = lvl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_default(level::level_enum lvl)
|
|
||||||
{
|
|
||||||
default_level_ = lvl;
|
|
||||||
}
|
|
||||||
|
|
||||||
level::level_enum get(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto it = levels_.find(logger_name);
|
|
||||||
return it != levels_.end() ? it->second : default_level_;
|
|
||||||
}
|
|
||||||
|
|
||||||
level::level_enum default_level()
|
|
||||||
{
|
|
||||||
return default_level_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@ -1,76 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace level {
|
|
||||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
|
||||||
|
|
||||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
|
||||||
|
|
||||||
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return level_string_views[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return short_level_names[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
int level = 0;
|
|
||||||
for (const auto &level_str : level_string_views)
|
|
||||||
{
|
|
||||||
if (level_str == name)
|
|
||||||
{
|
|
||||||
return static_cast<level::level_enum>(level);
|
|
||||||
}
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
// check also for "warn" and "err" before giving up..
|
|
||||||
if (name == "warn")
|
|
||||||
{
|
|
||||||
return level::warn;
|
|
||||||
}
|
|
||||||
if (name == "err")
|
|
||||||
{
|
|
||||||
return level::err;
|
|
||||||
}
|
|
||||||
return level::off;
|
|
||||||
}
|
|
||||||
} // namespace level
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
|
||||||
: msg_(std::move(msg))
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
|
||||||
{
|
|
||||||
memory_buf_t outbuf;
|
|
||||||
fmt::format_system_error(outbuf, last_errno, msg);
|
|
||||||
msg_ = fmt::to_string(outbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return msg_.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
|
|
||||||
{
|
|
||||||
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
|
|
||||||
{
|
|
||||||
SPDLOG_THROW(spdlog_ex(std::move(msg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,246 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/tweakme.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <memory>
|
|
||||||
#include <exception>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#ifdef SPDLOG_COMPILED_LIB
|
|
||||||
#undef SPDLOG_HEADER_ONLY
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
|
||||||
#ifdef spdlog_EXPORTS
|
|
||||||
#define SPDLOG_API __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_API __declspec(dllimport)
|
|
||||||
#endif
|
|
||||||
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
|
|
||||||
#define SPDLOG_API
|
|
||||||
#endif
|
|
||||||
#define SPDLOG_INLINE
|
|
||||||
#else // !defined(SPDLOG_COMPILED_LIB)
|
|
||||||
#define SPDLOG_API
|
|
||||||
#define SPDLOG_HEADER_ONLY
|
|
||||||
#define SPDLOG_INLINE inline
|
|
||||||
#endif // #ifdef SPDLOG_COMPILED_LIB
|
|
||||||
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
|
|
||||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
|
||||||
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
|
||||||
#define SPDLOG_CONSTEXPR
|
|
||||||
#else
|
|
||||||
#define SPDLOG_NOEXCEPT noexcept
|
|
||||||
#define SPDLOG_CONSTEXPR constexpr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_DEPRECATED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// disable thread local on msvc 2013
|
|
||||||
#ifndef SPDLOG_NO_TLS
|
|
||||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
|
||||||
#define SPDLOG_NO_TLS 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_FUNCTION
|
|
||||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SPDLOG_NO_EXCEPTIONS
|
|
||||||
#define SPDLOG_TRY
|
|
||||||
#define SPDLOG_THROW(ex) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
|
||||||
std::abort(); \
|
|
||||||
} while (0)
|
|
||||||
#define SPDLOG_CATCH_ALL()
|
|
||||||
#else
|
|
||||||
#define SPDLOG_TRY try
|
|
||||||
#define SPDLOG_THROW(ex) throw(ex)
|
|
||||||
#define SPDLOG_CATCH_ALL() catch (...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class formatter;
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
class sink;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
using filename_t = std::wstring;
|
|
||||||
#define SPDLOG_FILENAME_T(s) L##s
|
|
||||||
#else
|
|
||||||
using filename_t = std::string;
|
|
||||||
#define SPDLOG_FILENAME_T(s) s
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using log_clock = std::chrono::system_clock;
|
|
||||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
|
||||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
|
||||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
|
||||||
using string_view_t = fmt::basic_string_view<char>;
|
|
||||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
|
||||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
#ifndef _WIN32
|
|
||||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
|
|
||||||
{};
|
|
||||||
#endif // _WIN32
|
|
||||||
#else
|
|
||||||
template<typename>
|
|
||||||
struct is_convertible_to_wstring_view : std::false_type
|
|
||||||
{};
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
|
||||||
using level_t = details::null_atomic_int;
|
|
||||||
#else
|
|
||||||
using level_t = std::atomic<int>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SPDLOG_LEVEL_TRACE 0
|
|
||||||
#define SPDLOG_LEVEL_DEBUG 1
|
|
||||||
#define SPDLOG_LEVEL_INFO 2
|
|
||||||
#define SPDLOG_LEVEL_WARN 3
|
|
||||||
#define SPDLOG_LEVEL_ERROR 4
|
|
||||||
#define SPDLOG_LEVEL_CRITICAL 5
|
|
||||||
#define SPDLOG_LEVEL_OFF 6
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
|
||||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log level enum
|
|
||||||
namespace level {
|
|
||||||
enum level_enum
|
|
||||||
{
|
|
||||||
trace = SPDLOG_LEVEL_TRACE,
|
|
||||||
debug = SPDLOG_LEVEL_DEBUG,
|
|
||||||
info = SPDLOG_LEVEL_INFO,
|
|
||||||
warn = SPDLOG_LEVEL_WARN,
|
|
||||||
err = SPDLOG_LEVEL_ERROR,
|
|
||||||
critical = SPDLOG_LEVEL_CRITICAL,
|
|
||||||
off = SPDLOG_LEVEL_OFF,
|
|
||||||
n_levels
|
|
||||||
};
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
|
||||||
#define SPDLOG_LEVEL_NAMES \
|
|
||||||
{ \
|
|
||||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
|
||||||
|
|
||||||
#define SPDLOG_SHORT_LEVEL_NAMES \
|
|
||||||
{ \
|
|
||||||
"T", "D", "I", "W", "E", "C", "O" \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
|
||||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
|
||||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
using level_hasher = std::hash<int>;
|
|
||||||
} // namespace level
|
|
||||||
|
|
||||||
//
|
|
||||||
// Color mode used by sinks with color support.
|
|
||||||
//
|
|
||||||
enum class color_mode
|
|
||||||
{
|
|
||||||
always,
|
|
||||||
automatic,
|
|
||||||
never
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Pattern time - specific time getting to use for pattern_formatter.
|
|
||||||
// local time by default
|
|
||||||
//
|
|
||||||
enum class pattern_time_type
|
|
||||||
{
|
|
||||||
local, // log localtime
|
|
||||||
utc // log utc
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Log exception
|
|
||||||
//
|
|
||||||
class SPDLOG_API spdlog_ex : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit spdlog_ex(std::string msg);
|
|
||||||
spdlog_ex(const std::string &msg, int last_errno);
|
|
||||||
const char *what() const SPDLOG_NOEXCEPT override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string msg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
|
||||||
SPDLOG_API void throw_spdlog_ex(std::string msg);
|
|
||||||
|
|
||||||
struct source_loc
|
|
||||||
{
|
|
||||||
SPDLOG_CONSTEXPR source_loc() = default;
|
|
||||||
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
|
||||||
: filename{filename_in}
|
|
||||||
, line{line_in}
|
|
||||||
, funcname{funcname_in}
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return line == 0;
|
|
||||||
}
|
|
||||||
const char *filename{nullptr};
|
|
||||||
int line{0};
|
|
||||||
const char *funcname{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
// make_unique support for pre c++14
|
|
||||||
|
|
||||||
#if __cplusplus >= 201402L // C++14 and beyond
|
|
||||||
using std::make_unique;
|
|
||||||
#else
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
std::unique_ptr<T> make_unique(Args &&... args)
|
|
||||||
{
|
|
||||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
|
||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "common-inl.h"
|
|
||||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/backtracer.h>
|
|
||||||
#endif
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = other.messages_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = std::move(other.messages_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = std::move(other.messages_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::enable(size_t size)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
enabled_.store(true, std::memory_order_relaxed);
|
|
||||||
messages_ = circular_q<log_msg_buffer>{size};
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::disable()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
enabled_.store(false, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE bool backtracer::enabled() const
|
|
||||||
{
|
|
||||||
return enabled_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
messages_.push_back(log_msg_buffer{msg});
|
|
||||||
}
|
|
||||||
|
|
||||||
// pop all items in the q and apply the given fun on each of them.
|
|
||||||
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
while (!messages_.empty())
|
|
||||||
{
|
|
||||||
auto &front_msg = messages_.front();
|
|
||||||
fun(front_msg);
|
|
||||||
messages_.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
#include <spdlog/details/circular_q.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
// Store log messages in circular buffer.
|
|
||||||
// Useful for storing debug data in case of error/warning happens.
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
class SPDLOG_API backtracer
|
|
||||||
{
|
|
||||||
mutable std::mutex mutex_;
|
|
||||||
std::atomic<bool> enabled_{false};
|
|
||||||
circular_q<log_msg_buffer> messages_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
backtracer() = default;
|
|
||||||
backtracer(const backtracer &other);
|
|
||||||
|
|
||||||
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
backtracer &operator=(backtracer other);
|
|
||||||
|
|
||||||
void enable(size_t size);
|
|
||||||
void disable();
|
|
||||||
bool enabled() const;
|
|
||||||
void push_back(const log_msg &msg);
|
|
||||||
|
|
||||||
// pop all items in the q and apply the given fun on each of them.
|
|
||||||
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "backtracer-inl.h"
|
|
||||||
#endif
|
|
@ -1,141 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
// circular q view of std::vector.
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
template<typename T>
|
|
||||||
class circular_q
|
|
||||||
{
|
|
||||||
size_t max_items_ = 0;
|
|
||||||
typename std::vector<T>::size_type head_ = 0;
|
|
||||||
typename std::vector<T>::size_type tail_ = 0;
|
|
||||||
size_t overrun_counter_ = 0;
|
|
||||||
std::vector<T> v_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = T;
|
|
||||||
|
|
||||||
// empty ctor - create a disabled queue with no elements allocated at all
|
|
||||||
circular_q() = default;
|
|
||||||
|
|
||||||
explicit circular_q(size_t max_items)
|
|
||||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
|
||||||
, v_(max_items_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
circular_q(const circular_q &) = default;
|
|
||||||
circular_q &operator=(const circular_q &) = default;
|
|
||||||
|
|
||||||
// move cannot be default,
|
|
||||||
// since we need to reset head_, tail_, etc to zero in the moved object
|
|
||||||
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
copy_moveable(std::move(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
copy_moveable(std::move(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push back, overrun (oldest) item if no room left
|
|
||||||
void push_back(T &&item)
|
|
||||||
{
|
|
||||||
if (max_items_ > 0)
|
|
||||||
{
|
|
||||||
v_[tail_] = std::move(item);
|
|
||||||
tail_ = (tail_ + 1) % max_items_;
|
|
||||||
|
|
||||||
if (tail_ == head_) // overrun last item if full
|
|
||||||
{
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
++overrun_counter_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return reference to the front item.
|
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
|
||||||
const T &front() const
|
|
||||||
{
|
|
||||||
return v_[head_];
|
|
||||||
}
|
|
||||||
|
|
||||||
T &front()
|
|
||||||
{
|
|
||||||
return v_[head_];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return number of elements actually stored
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
if (tail_ >= head_)
|
|
||||||
{
|
|
||||||
return tail_ - head_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return max_items_ - (head_ - tail_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return const reference to item by index.
|
|
||||||
// If index is out of range 0…size()-1, the behavior is undefined.
|
|
||||||
const T &at(size_t i) const
|
|
||||||
{
|
|
||||||
assert(i < size());
|
|
||||||
return v_[(head_ + i) % max_items_];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop item from front.
|
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
|
||||||
void pop_front()
|
|
||||||
{
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return tail_ == head_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool full() const
|
|
||||||
{
|
|
||||||
// head is ahead of the tail by 1
|
|
||||||
if (max_items_ > 0)
|
|
||||||
{
|
|
||||||
return ((tail_ + 1) % max_items_) == head_;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t overrun_counter() const
|
|
||||||
{
|
|
||||||
return overrun_counter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// copy from other&& and reset it to disabled state
|
|
||||||
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
max_items_ = other.max_items_;
|
|
||||||
head_ = other.head_;
|
|
||||||
tail_ = other.tail_;
|
|
||||||
overrun_counter_ = other.overrun_counter_;
|
|
||||||
v_ = std::move(other.v_);
|
|
||||||
|
|
||||||
// put &&other in disabled, but valid state
|
|
||||||
other.max_items_ = 0;
|
|
||||||
other.head_ = other.tail_ = 0;
|
|
||||||
other.overrun_counter_ = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,32 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
struct console_mutex
|
|
||||||
{
|
|
||||||
using mutex_t = std::mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_nullmutex
|
|
||||||
{
|
|
||||||
using mutex_t = null_mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,132 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE file_helper::~file_helper()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
filename_ = fname;
|
|
||||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
|
||||||
|
|
||||||
for (int tries = 0; tries < open_tries_; ++tries)
|
|
||||||
{
|
|
||||||
// create containing folder if not exists already.
|
|
||||||
os::create_dir(os::dir_name(fname));
|
|
||||||
if (!os::fopen_s(&fd_, fname, mode))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::os::sleep_for_millis(open_interval_);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
|
||||||
{
|
|
||||||
if (filename_.empty())
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Failed re opening file - was not opened before");
|
|
||||||
}
|
|
||||||
this->open(filename_, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::flush()
|
|
||||||
{
|
|
||||||
std::fflush(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::close()
|
|
||||||
{
|
|
||||||
if (fd_ != nullptr)
|
|
||||||
{
|
|
||||||
std::fclose(fd_);
|
|
||||||
fd_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
|
||||||
{
|
|
||||||
size_t msg_size = buf.size();
|
|
||||||
auto data = buf.data();
|
|
||||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE size_t file_helper::size() const
|
|
||||||
{
|
|
||||||
if (fd_ == nullptr)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
|
||||||
}
|
|
||||||
return os::filesize(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const filename_t &file_helper::filename() const
|
|
||||||
{
|
|
||||||
return filename_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// return file path and its extension:
|
|
||||||
//
|
|
||||||
// "mylog.txt" => ("mylog", ".txt")
|
|
||||||
// "mylog" => ("mylog", "")
|
|
||||||
// "mylog." => ("mylog.", "")
|
|
||||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
|
||||||
//
|
|
||||||
// the starting dot in filenames is ignored (hidden files):
|
|
||||||
//
|
|
||||||
// ".mylog" => (".mylog". "")
|
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
|
||||||
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
|
|
||||||
{
|
|
||||||
auto ext_index = fname.rfind('.');
|
|
||||||
|
|
||||||
// no valid extension found - return whole path and empty string as
|
|
||||||
// extension
|
|
||||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
|
||||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
|
||||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally - return a valid base and extension tuple
|
|
||||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,59 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// Helper class for file sinks.
|
|
||||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
|
||||||
// Throw spdlog_ex exception on errors.
|
|
||||||
|
|
||||||
class SPDLOG_API file_helper
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit file_helper() = default;
|
|
||||||
|
|
||||||
file_helper(const file_helper &) = delete;
|
|
||||||
file_helper &operator=(const file_helper &) = delete;
|
|
||||||
~file_helper();
|
|
||||||
|
|
||||||
void open(const filename_t &fname, bool truncate = false);
|
|
||||||
void reopen(bool truncate);
|
|
||||||
void flush();
|
|
||||||
void close();
|
|
||||||
void write(const memory_buf_t &buf);
|
|
||||||
size_t size() const;
|
|
||||||
const filename_t &filename() const;
|
|
||||||
|
|
||||||
//
|
|
||||||
// return file path and its extension:
|
|
||||||
//
|
|
||||||
// "mylog.txt" => ("mylog", ".txt")
|
|
||||||
// "mylog" => ("mylog", "")
|
|
||||||
// "mylog." => ("mylog.", "")
|
|
||||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
|
||||||
//
|
|
||||||
// the starting dot in filenames is ignored (hidden files):
|
|
||||||
//
|
|
||||||
// ".mylog" => (".mylog". "")
|
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
|
||||||
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int open_tries_ = 5;
|
|
||||||
const int open_interval_ = 10;
|
|
||||||
std::FILE *fd_{nullptr};
|
|
||||||
filename_t filename_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "file_helper-inl.h"
|
|
||||||
#endif
|
|
@ -1,116 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
// Some fmt helpers to efficiently format and pad ints and strings
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace fmt_helper {
|
|
||||||
|
|
||||||
inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return spdlog::string_view_t{buf.data(), buf.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
auto *buf_ptr = view.data();
|
|
||||||
dest.append(buf_ptr, buf_ptr + view.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void append_int(T n, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
fmt::format_int i(n);
|
|
||||||
dest.append(i.data(), i.data() + i.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline unsigned int count_digits(T n)
|
|
||||||
{
|
|
||||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
|
||||||
return static_cast<unsigned int>(fmt::
|
|
||||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
|
||||||
// See: https://github.com/fmtlib/fmt/issues/1538
|
|
||||||
#if FMT_VERSION < 70000
|
|
||||||
internal
|
|
||||||
#else
|
|
||||||
detail
|
|
||||||
#endif
|
|
||||||
::count_digits(static_cast<count_type>(n)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void pad2(int n, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
if (n >= 0 && n < 100) // 0-99
|
|
||||||
{
|
|
||||||
dest.push_back(static_cast<char>('0' + n / 10));
|
|
||||||
dest.push_back(static_cast<char>('0' + n % 10));
|
|
||||||
}
|
|
||||||
else // unlikely, but just in case, let fmt deal with it
|
|
||||||
{
|
|
||||||
fmt::format_to(dest, "{:02}", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
|
||||||
for (auto digits = count_digits(n); digits < width; digits++)
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
}
|
|
||||||
append_int(n, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void pad3(T n, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
|
||||||
if (n < 1000)
|
|
||||||
{
|
|
||||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
|
||||||
n = n % 100;
|
|
||||||
dest.push_back(static_cast<char>((n / 10) + '0'));
|
|
||||||
dest.push_back(static_cast<char>((n % 10) + '0'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
append_int(n, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void pad6(T n, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
pad_uint(n, 6, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void pad9(T n, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
pad_uint(n, 9, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return fraction of a second of the given time_point.
|
|
||||||
// e.g.
|
|
||||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
|
||||||
template<typename ToDuration>
|
|
||||||
inline ToDuration time_fraction(log_clock::time_point tp)
|
|
||||||
{
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::seconds;
|
|
||||||
auto duration = tp.time_since_epoch();
|
|
||||||
auto secs = duration_cast<seconds>(duration);
|
|
||||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fmt_helper
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,37 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
|
|
||||||
spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: logger_name(a_logger_name)
|
|
||||||
, level(lvl)
|
|
||||||
, time(log_time)
|
|
||||||
#ifndef SPDLOG_NO_THREAD_ID
|
|
||||||
, thread_id(os::thread_id())
|
|
||||||
#endif
|
|
||||||
, source(loc)
|
|
||||||
, payload(msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(
|
|
||||||
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: log_msg(os::now(), loc, a_logger_name, lvl, msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,36 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
struct SPDLOG_API log_msg
|
|
||||||
{
|
|
||||||
log_msg() = default;
|
|
||||||
log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
|
||||||
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
|
||||||
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
|
||||||
log_msg(const log_msg &other) = default;
|
|
||||||
|
|
||||||
string_view_t logger_name;
|
|
||||||
level::level_enum level{level::off};
|
|
||||||
log_clock::time_point time;
|
|
||||||
size_t thread_id{0};
|
|
||||||
|
|
||||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
|
||||||
mutable size_t color_range_start{0};
|
|
||||||
mutable size_t color_range_end{0};
|
|
||||||
|
|
||||||
source_loc source;
|
|
||||||
string_view_t payload;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "log_msg-inl.h"
|
|
||||||
#endif
|
|
@ -1,58 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
|
||||||
: log_msg{orig_msg}
|
|
||||||
{
|
|
||||||
buffer.append(logger_name.begin(), logger_name.end());
|
|
||||||
buffer.append(payload.begin(), payload.end());
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
|
||||||
: log_msg{other}
|
|
||||||
{
|
|
||||||
buffer.append(logger_name.begin(), logger_name.end());
|
|
||||||
buffer.append(payload.begin(), payload.end());
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
|
|
||||||
{
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
|
|
||||||
{
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer.clear();
|
|
||||||
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer = std::move(other.buffer);
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void log_msg_buffer::update_string_views()
|
|
||||||
{
|
|
||||||
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
|
||||||
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,33 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// Extend log_msg with internal buffer to store its payload.
|
|
||||||
// This is needed since log_msg holds string_views that points to stack data.
|
|
||||||
|
|
||||||
class SPDLOG_API log_msg_buffer : public log_msg
|
|
||||||
{
|
|
||||||
memory_buf_t buffer;
|
|
||||||
void update_string_views();
|
|
||||||
|
|
||||||
public:
|
|
||||||
log_msg_buffer() = default;
|
|
||||||
explicit log_msg_buffer(const log_msg &orig_msg);
|
|
||||||
log_msg_buffer(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
log_msg_buffer &operator=(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "log_msg_buffer-inl.h"
|
|
||||||
#endif
|
|
@ -1,120 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// multi producer-multi consumer blocking queue.
|
|
||||||
// enqueue(..) - will block until room found to put the new message.
|
|
||||||
// enqueue_nowait(..) - will return immediately with false if no room left in
|
|
||||||
// the queue.
|
|
||||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
|
||||||
// passed.
|
|
||||||
|
|
||||||
#include <spdlog/details/circular_q.h>
|
|
||||||
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class mpmc_blocking_queue
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using item_type = T;
|
|
||||||
explicit mpmc_blocking_queue(size_t max_items)
|
|
||||||
: q_(max_items)
|
|
||||||
{}
|
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
// try to enqueue and block if no room left
|
|
||||||
void enqueue(T &&item)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
}
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
|
||||||
void enqueue_nowait(T &&item)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
}
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
popped_item = std::move(q_.front());
|
|
||||||
q_.pop_front();
|
|
||||||
}
|
|
||||||
pop_cv_.notify_one();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
|
||||||
// so release the mutex at the very end each function.
|
|
||||||
|
|
||||||
// try to enqueue and block if no room left
|
|
||||||
void enqueue(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
|
||||||
void enqueue_nowait(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
popped_item = std::move(q_.front());
|
|
||||||
q_.pop_front();
|
|
||||||
pop_cv_.notify_one();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t overrun_counter()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
return q_.overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::mutex queue_mutex_;
|
|
||||||
std::condition_variable push_cv_;
|
|
||||||
std::condition_variable pop_cv_;
|
|
||||||
spdlog::details::circular_q<T> q_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <utility>
|
|
||||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
struct null_mutex
|
|
||||||
{
|
|
||||||
void lock() const {}
|
|
||||||
void unlock() const {}
|
|
||||||
bool try_lock() const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct null_atomic_int
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
null_atomic_int() = default;
|
|
||||||
|
|
||||||
explicit null_atomic_int(int new_value)
|
|
||||||
: value(new_value)
|
|
||||||
{}
|
|
||||||
|
|
||||||
int load(std::memory_order = std::memory_order_relaxed) const
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void store(int new_value, std::memory_order = std::memory_order_relaxed)
|
|
||||||
{
|
|
||||||
value = new_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
|
|
||||||
{
|
|
||||||
std::swap(new_value, value);
|
|
||||||
return new_value; // return value before the call
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,554 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <array>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include <io.h> // _get_osfhandle and _isatty support
|
|
||||||
#include <process.h> // _get_pid support
|
|
||||||
#include <spdlog/details/windows_include.h>
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
#include <share.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
#include <limits>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <direct.h> // for _mkdir/_wmkdir
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
|
||||||
|
|
||||||
#elif defined(_AIX)
|
|
||||||
#include <pthread.h> // for pthread_getthreadid_np
|
|
||||||
|
|
||||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
|
||||||
#include <pthread_np.h> // for pthread_getthreadid_np
|
|
||||||
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
#include <lwp.h> // for _lwp_self
|
|
||||||
|
|
||||||
#elif defined(__sun)
|
|
||||||
#include <thread.h> // for thr_self
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // unix
|
|
||||||
|
|
||||||
#ifndef __has_feature // Clang - feature checking macros.
|
|
||||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace os {
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
|
||||||
timespec ts;
|
|
||||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
|
||||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
|
||||||
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
|
||||||
|
|
||||||
#else
|
|
||||||
return log_clock::now();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::tm tm;
|
|
||||||
::localtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
::localtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::time_t now_t = ::time(nullptr);
|
|
||||||
return localtime(now_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::tm tm;
|
|
||||||
::gmtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
::gmtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::time_t now_t = ::time(nullptr);
|
|
||||||
return gmtime(now_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fopen_s on non windows for writing
|
|
||||||
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
|
||||||
#else
|
|
||||||
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
|
||||||
#endif
|
|
||||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
|
||||||
if (*fp != nullptr)
|
|
||||||
{
|
|
||||||
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
|
|
||||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
|
||||||
{
|
|
||||||
::fclose(*fp);
|
|
||||||
*fp = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else // unix
|
|
||||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
|
||||||
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
|
|
||||||
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
|
||||||
if (fd == -1)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*fp = ::fdopen(fd, mode.c_str());
|
|
||||||
if (*fp == nullptr)
|
|
||||||
{
|
|
||||||
::close(fd);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return *fp == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
return ::_wremove(filename.c_str());
|
|
||||||
#else
|
|
||||||
return std::remove(filename.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return path_exists(filename) ? remove(filename) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
return ::_wrename(filename1.c_str(), filename2.c_str());
|
|
||||||
#else
|
|
||||||
return std::rename(filename1.c_str(), filename2.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if path exists (file or directory)
|
|
||||||
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
auto attribs = ::GetFileAttributesW(filename.c_str());
|
|
||||||
#else
|
|
||||||
auto attribs = ::GetFileAttributesA(filename.c_str());
|
|
||||||
#endif
|
|
||||||
return attribs != INVALID_FILE_ATTRIBUTES;
|
|
||||||
#else // common linux/unix all have the stat system call
|
|
||||||
struct stat buffer;
|
|
||||||
return (::stat(filename.c_str(), &buffer) == 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return file size according to open FILE* object
|
|
||||||
SPDLOG_INLINE size_t filesize(FILE *f)
|
|
||||||
{
|
|
||||||
if (f == nullptr)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Failed getting file size. fd is null");
|
|
||||||
}
|
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
||||||
int fd = ::_fileno(f);
|
|
||||||
#if _WIN64 // 64 bits
|
|
||||||
__int64 ret = ::_filelengthi64(fd);
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // windows 32 bits
|
|
||||||
long ret = ::_filelength(fd);
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
// OpenBSD doesn't compile with :: before the fileno(..)
|
|
||||||
#if defined(__OpenBSD__)
|
|
||||||
int fd = fileno(f);
|
|
||||||
#else
|
|
||||||
int fd = ::fileno(f);
|
|
||||||
#endif
|
|
||||||
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
|
||||||
#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
|
|
||||||
struct stat64 st;
|
|
||||||
if (::fstat64(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#else // other unix or linux 32 bits or cygwin
|
|
||||||
struct stat st;
|
|
||||||
if (::fstat(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
throw_spdlog_ex("Failed getting file size from fd", errno);
|
|
||||||
return 0; // will not be reached.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
|
||||||
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
|
||||||
TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = ::GetTimeZoneInformation(&tzinfo);
|
|
||||||
#else
|
|
||||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
|
|
||||||
#endif
|
|
||||||
if (rv == TIME_ZONE_ID_INVALID)
|
|
||||||
throw_spdlog_ex("Failed getting timezone info. ", errno);
|
|
||||||
|
|
||||||
int offset = -tzinfo.Bias;
|
|
||||||
if (tm.tm_isdst)
|
|
||||||
{
|
|
||||||
offset -= tzinfo.DaylightBias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset -= tzinfo.StandardBias;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
|
|
||||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
|
||||||
struct helper
|
|
||||||
{
|
|
||||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
|
||||||
{
|
|
||||||
int local_year = localtm.tm_year + (1900 - 1);
|
|
||||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
|
||||||
|
|
||||||
long int days = (
|
|
||||||
// difference in day of year
|
|
||||||
localtm.tm_yday -
|
|
||||||
gmtm.tm_yday
|
|
||||||
|
|
||||||
// + intervening leap days
|
|
||||||
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
|
||||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
|
||||||
|
|
||||||
// + difference in years * 365 */
|
|
||||||
+ (long int)(local_year - gmt_year) * 365);
|
|
||||||
|
|
||||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
|
||||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
|
||||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
|
||||||
|
|
||||||
return secs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
|
||||||
#else
|
|
||||||
auto offset_seconds = tm.tm_gmtoff;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return static_cast<int>(offset_seconds / 60);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t
|
|
||||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
|
||||||
// under VS 2013)
|
|
||||||
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<size_t>(::GetCurrentThreadId());
|
|
||||||
#elif defined(__linux__)
|
|
||||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
|
||||||
#define SYS_gettid __NR_gettid
|
|
||||||
#endif
|
|
||||||
return static_cast<size_t>(::syscall(SYS_gettid));
|
|
||||||
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
|
|
||||||
return static_cast<size_t>(::pthread_getthreadid_np());
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
return static_cast<size_t>(::_lwp_self());
|
|
||||||
#elif defined(__OpenBSD__)
|
|
||||||
return static_cast<size_t>(::getthrid());
|
|
||||||
#elif defined(__sun)
|
|
||||||
return static_cast<size_t>(::thr_self());
|
|
||||||
#elif __APPLE__
|
|
||||||
uint64_t tid;
|
|
||||||
pthread_threadid_np(nullptr, &tid);
|
|
||||||
return static_cast<size_t>(tid);
|
|
||||||
#else // Default to standard C++11 (other Unix)
|
|
||||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t (from thread local storage)
|
|
||||||
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_NO_TLS)
|
|
||||||
return _thread_id();
|
|
||||||
#else // cache thread id in tls
|
|
||||||
static thread_local const size_t tid = _thread_id();
|
|
||||||
return tid;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
|
||||||
// See https://github.com/gabime/spdlog/issues/609
|
|
||||||
SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
::Sleep(milliseconds);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
memory_buf_t buf;
|
|
||||||
wstr_to_utf8buf(filename, buf);
|
|
||||||
return fmt::to_string(buf);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<int>(::GetCurrentProcessId());
|
|
||||||
#else
|
|
||||||
return static_cast<int>(::getpid());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the terminal supports colors
|
|
||||||
// Based on: https://github.com/agauniyal/rang/
|
|
||||||
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
static constexpr std::array<const char *, 14> terms = {
|
|
||||||
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
|
||||||
|
|
||||||
const char *env_p = std::getenv("TERM");
|
|
||||||
if (env_p == nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const bool result =
|
|
||||||
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the terminal attached
|
|
||||||
// Source: https://github.com/agauniyal/rang/
|
|
||||||
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return ::_isatty(_fileno(file)) != 0;
|
|
||||||
#else
|
|
||||||
return ::isatty(fileno(file)) != 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
|
||||||
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
|
||||||
{
|
|
||||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
|
||||||
}
|
|
||||||
|
|
||||||
int wstr_size = static_cast<int>(wstr.size());
|
|
||||||
if (wstr_size == 0)
|
|
||||||
{
|
|
||||||
target.resize(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int result_size = static_cast<int>(target.capacity());
|
|
||||||
if ((wstr_size + 1) * 2 > result_size)
|
|
||||||
{
|
|
||||||
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result_size > 0)
|
|
||||||
{
|
|
||||||
target.resize(result_size);
|
|
||||||
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
|
|
||||||
|
|
||||||
if (result_size > 0)
|
|
||||||
{
|
|
||||||
target.resize(result_size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
|
||||||
}
|
|
||||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
|
||||||
|
|
||||||
// return true on success
|
|
||||||
static SPDLOG_INLINE bool mkdir_(const filename_t &path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
return ::_wmkdir(path.c_str()) == 0;
|
|
||||||
#else
|
|
||||||
return ::_mkdir(path.c_str()) == 0;
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
return ::mkdir(path.c_str(), mode_t(0755)) == 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the given directory - and all directories leading to it
|
|
||||||
// return true on success or if the directory already exists
|
|
||||||
SPDLOG_INLINE bool create_dir(filename_t path)
|
|
||||||
{
|
|
||||||
if (path_exists(path))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.empty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
// support forward slash in windows
|
|
||||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t search_offset = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
auto token_pos = path.find(folder_sep, search_offset);
|
|
||||||
// treat the entire path as a folder if no folder separator not found
|
|
||||||
if (token_pos == filename_t::npos)
|
|
||||||
{
|
|
||||||
token_pos = path.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto subdir = path.substr(0, token_pos);
|
|
||||||
|
|
||||||
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir))
|
|
||||||
{
|
|
||||||
return false; // return error if failed creating dir
|
|
||||||
}
|
|
||||||
search_offset = token_pos + 1;
|
|
||||||
} while (search_offset < path.size());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return directory name from given path or empty string
|
|
||||||
// "abc/file" => "abc"
|
|
||||||
// "abc/" => "abc"
|
|
||||||
// "abc" => ""
|
|
||||||
// "abc///" => "abc//"
|
|
||||||
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// support forward slash in windows
|
|
||||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
|
||||||
#endif
|
|
||||||
auto pos = path.find_last_of(folder_sep);
|
|
||||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SPDLOG_INLINE getenv(const char *field)
|
|
||||||
{
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#if defined(__cplusplus_winrt)
|
|
||||||
return std::string{}; // not supported under uwp
|
|
||||||
#else
|
|
||||||
size_t len = 0;
|
|
||||||
char buf[128];
|
|
||||||
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
|
||||||
return ok ? buf : std::string{};
|
|
||||||
#endif
|
|
||||||
#else // revert to getenv
|
|
||||||
char *buf = ::getenv(field);
|
|
||||||
return buf ? buf : std::string{};
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace os
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,111 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <ctime> // std::time_t
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace os {
|
|
||||||
|
|
||||||
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// eol definition
|
|
||||||
#if !defined(SPDLOG_EOL)
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define SPDLOG_EOL "\r\n"
|
|
||||||
#else
|
|
||||||
#define SPDLOG_EOL "\n"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
|
||||||
|
|
||||||
// folder separator
|
|
||||||
#ifdef _WIN32
|
|
||||||
static const char folder_sep = '\\';
|
|
||||||
#else
|
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// fopen_s on non windows for writing
|
|
||||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
|
||||||
|
|
||||||
// Remove filename. return 0 on success
|
|
||||||
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Remove file if exists. return 0 on success
|
|
||||||
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
|
||||||
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Return if file exists.
|
|
||||||
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Return file size according to open FILE* object
|
|
||||||
SPDLOG_API size_t filesize(FILE *f);
|
|
||||||
|
|
||||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
|
||||||
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
|
||||||
|
|
||||||
// Return current thread id as size_t
|
|
||||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
|
||||||
// under VS 2013)
|
|
||||||
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Return current thread id as size_t (from thread local storage)
|
|
||||||
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
|
||||||
// See https://github.com/gabime/spdlog/issues/609
|
|
||||||
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
|
||||||
|
|
||||||
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Determine if the terminal supports colors
|
|
||||||
// Source: https://github.com/agauniyal/rang/
|
|
||||||
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// Determine if the terminal attached
|
|
||||||
// Source: https://github.com/agauniyal/rang/
|
|
||||||
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
|
||||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Return directory name from given path or empty string
|
|
||||||
// "abc/file" => "abc"
|
|
||||||
// "abc/" => "abc"
|
|
||||||
// "abc" => ""
|
|
||||||
// "abc///" => "abc//"
|
|
||||||
SPDLOG_API filename_t dir_name(filename_t path);
|
|
||||||
|
|
||||||
// Create a dir from the given path.
|
|
||||||
// Return true if succeeded or if this dir already exists.
|
|
||||||
SPDLOG_API bool create_dir(filename_t path);
|
|
||||||
|
|
||||||
// non thread safe, cross platform getenv/getenv_s
|
|
||||||
// return empty string if field not found
|
|
||||||
SPDLOG_API std::string getenv(const char *field);
|
|
||||||
|
|
||||||
} // namespace os
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "os-inl.h"
|
|
||||||
#endif
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/periodic_worker.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE periodic_worker::periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
|
|
||||||
{
|
|
||||||
active_ = (interval > std::chrono::seconds::zero());
|
|
||||||
if (!active_)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
|
||||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
|
||||||
{
|
|
||||||
return; // active_ == false, so exit this thread
|
|
||||||
}
|
|
||||||
callback_fun();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// stop the worker thread and join it
|
|
||||||
SPDLOG_INLINE periodic_worker::~periodic_worker()
|
|
||||||
{
|
|
||||||
if (worker_thread_.joinable())
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
active_ = false;
|
|
||||||
}
|
|
||||||
cv_.notify_one();
|
|
||||||
worker_thread_.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// periodic worker thread - periodically executes the given callback function.
|
|
||||||
//
|
|
||||||
// RAII over the owned thread:
|
|
||||||
// creates the thread on construction.
|
|
||||||
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
class SPDLOG_API periodic_worker
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);
|
|
||||||
periodic_worker(const periodic_worker &) = delete;
|
|
||||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
|
||||||
// stop the worker thread and join it
|
|
||||||
~periodic_worker();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool active_;
|
|
||||||
std::thread worker_thread_;
|
|
||||||
std::mutex mutex_;
|
|
||||||
std::condition_variable cv_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "periodic_worker-inl.h"
|
|
||||||
#endif
|
|
@ -1,299 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/periodic_worker.h>
|
|
||||||
#include <spdlog/logger.h>
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
|
|
||||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
// support for the default stdout color logger
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <spdlog/sinks/wincolor_sink.h>
|
|
||||||
#else
|
|
||||||
#include <spdlog/sinks/ansicolor_sink.h>
|
|
||||||
#endif
|
|
||||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE registry::registry()
|
|
||||||
: formatter_(new pattern_formatter())
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
|
||||||
#ifdef _WIN32
|
|
||||||
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
|
||||||
#else
|
|
||||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char *default_logger_name = "";
|
|
||||||
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
|
||||||
loggers_[default_logger_name] = default_logger_;
|
|
||||||
|
|
||||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE registry::~registry() = default;
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
register_logger_(std::move(new_logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
new_logger->set_formatter(formatter_->clone());
|
|
||||||
|
|
||||||
if (err_handler_)
|
|
||||||
{
|
|
||||||
new_logger->set_error_handler(err_handler_);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_logger->set_level(levels_.get(new_logger->name()));
|
|
||||||
new_logger->flush_on(flush_level_);
|
|
||||||
|
|
||||||
if (backtrace_n_messages_ > 0)
|
|
||||||
{
|
|
||||||
new_logger->enable_backtrace(backtrace_n_messages_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (automatic_registration_)
|
|
||||||
{
|
|
||||||
register_logger_(std::move(new_logger));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
auto found = loggers_.find(logger_name);
|
|
||||||
return found == loggers_.end() ? nullptr : found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
return default_logger_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return raw ptr to the default logger.
|
|
||||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
|
||||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
|
||||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
|
||||||
SPDLOG_INLINE logger *registry::get_default_raw()
|
|
||||||
{
|
|
||||||
return default_logger_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// set default logger.
|
|
||||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
|
||||||
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
// remove previous default logger from the map
|
|
||||||
if (default_logger_ != nullptr)
|
|
||||||
{
|
|
||||||
loggers_.erase(default_logger_->name());
|
|
||||||
}
|
|
||||||
if (new_default_logger != nullptr)
|
|
||||||
{
|
|
||||||
loggers_[new_default_logger->name()] = new_default_logger;
|
|
||||||
}
|
|
||||||
default_logger_ = std::move(new_default_logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
tp_ = std::move(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
return tp_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
|
||||||
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
formatter_ = std::move(formatter);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_formatter(formatter_->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
backtrace_n_messages_ = n_messages;
|
|
||||||
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->enable_backtrace(n_messages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::disable_backtrace()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
backtrace_n_messages_ = 0;
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->disable_backtrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_level(log_level);
|
|
||||||
}
|
|
||||||
levels_.set_default(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush_on(log_level);
|
|
||||||
}
|
|
||||||
flush_level_ = log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
auto clbk = [this]() { this->flush_all(); };
|
|
||||||
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg))
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_error_handler(handler);
|
|
||||||
}
|
|
||||||
err_handler_ = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
fun(l.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::flush_all()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::drop(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.erase(logger_name);
|
|
||||||
if (default_logger_ && default_logger_->name() == logger_name)
|
|
||||||
{
|
|
||||||
default_logger_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::drop_all()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.clear();
|
|
||||||
default_logger_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean all resources and threads started by the registry
|
|
||||||
SPDLOG_INLINE void registry::shutdown()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
periodic_flusher_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_all();
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
tp_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex()
|
|
||||||
{
|
|
||||||
return tp_mutex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
automatic_registration_ = automatic_registration;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
levels_ = std::move(levels);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
auto &logger = l.second;
|
|
||||||
logger->set_level(levels_.get(logger->name()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE registry ®istry::instance()
|
|
||||||
{
|
|
||||||
static registry s_instance;
|
|
||||||
return s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
if (loggers_.find(logger_name) != loggers_.end())
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
|
|
||||||
{
|
|
||||||
auto logger_name = new_logger->name();
|
|
||||||
throw_if_exists_(logger_name);
|
|
||||||
loggers_[logger_name] = std::move(new_logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,112 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Loggers registry of unique name->logger pointer
|
|
||||||
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
|
||||||
// If user requests a non existing logger, nullptr will be returned
|
|
||||||
// This class is thread safe
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/cfg/log_levels.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
class logger;
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
class thread_pool;
|
|
||||||
class periodic_worker;
|
|
||||||
|
|
||||||
class SPDLOG_API registry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
registry(const registry &) = delete;
|
|
||||||
registry &operator=(const registry &) = delete;
|
|
||||||
|
|
||||||
void register_logger(std::shared_ptr<logger> new_logger);
|
|
||||||
void initialize_logger(std::shared_ptr<logger> new_logger);
|
|
||||||
std::shared_ptr<logger> get(const std::string &logger_name);
|
|
||||||
std::shared_ptr<logger> default_logger();
|
|
||||||
|
|
||||||
// Return raw ptr to the default logger.
|
|
||||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
|
||||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
|
||||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
|
||||||
logger *get_default_raw();
|
|
||||||
|
|
||||||
// set default logger.
|
|
||||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
|
||||||
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
|
||||||
|
|
||||||
void set_tp(std::shared_ptr<thread_pool> tp);
|
|
||||||
|
|
||||||
std::shared_ptr<thread_pool> get_tp();
|
|
||||||
|
|
||||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
|
||||||
void set_formatter(std::unique_ptr<formatter> formatter);
|
|
||||||
|
|
||||||
void enable_backtrace(size_t n_messages);
|
|
||||||
|
|
||||||
void disable_backtrace();
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level);
|
|
||||||
|
|
||||||
void flush_on(level::level_enum log_level);
|
|
||||||
|
|
||||||
void flush_every(std::chrono::seconds interval);
|
|
||||||
|
|
||||||
void set_error_handler(void (*handler)(const std::string &msg));
|
|
||||||
|
|
||||||
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
|
|
||||||
|
|
||||||
void flush_all();
|
|
||||||
|
|
||||||
void drop(const std::string &logger_name);
|
|
||||||
|
|
||||||
void drop_all();
|
|
||||||
|
|
||||||
// clean all resources and threads started by the registry
|
|
||||||
void shutdown();
|
|
||||||
|
|
||||||
std::recursive_mutex &tp_mutex();
|
|
||||||
|
|
||||||
void set_automatic_registration(bool automatic_registration);
|
|
||||||
|
|
||||||
void update_levels(cfg::log_levels levels);
|
|
||||||
|
|
||||||
static registry &instance();
|
|
||||||
|
|
||||||
private:
|
|
||||||
registry();
|
|
||||||
~registry();
|
|
||||||
|
|
||||||
void throw_if_exists_(const std::string &logger_name);
|
|
||||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
|
||||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
|
||||||
std::recursive_mutex tp_mutex_;
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
|
||||||
cfg::log_levels levels_;
|
|
||||||
std::unique_ptr<formatter> formatter_;
|
|
||||||
level::level_enum flush_level_ = level::off;
|
|
||||||
void (*err_handler_)(const std::string &msg);
|
|
||||||
std::shared_ptr<thread_pool> tp_;
|
|
||||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
|
||||||
std::shared_ptr<logger> default_logger_;
|
|
||||||
bool automatic_registration_ = true;
|
|
||||||
size_t backtrace_n_messages_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "registry-inl.h"
|
|
||||||
#endif
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "registry.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// Default logger factory- creates synchronous loggers
|
|
||||||
class logger;
|
|
||||||
|
|
||||||
struct synchronous_factory
|
|
||||||
{
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
|
||||||
{
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
|
||||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
|
||||||
details::registry::instance().initialize_logger(new_logger);
|
|
||||||
return new_logger;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
@ -1,175 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
// tcp client helper
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#pragma comment(lib, "Ws2_32.lib")
|
|
||||||
#pragma comment(lib, "Mswsock.lib")
|
|
||||||
#pragma comment(lib, "AdvApi32.lib")
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
class tcp_client
|
|
||||||
{
|
|
||||||
SOCKET socket_ = INVALID_SOCKET;
|
|
||||||
|
|
||||||
static bool winsock_initialized_()
|
|
||||||
{
|
|
||||||
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
if (s == INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
closesocket(s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_winsock_()
|
|
||||||
{
|
|
||||||
WSADATA wsaData;
|
|
||||||
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
||||||
if (rv != 0)
|
|
||||||
{
|
|
||||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
|
||||||
{
|
|
||||||
char buf[512];
|
|
||||||
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
|
||||||
|
|
||||||
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool is_connected() const
|
|
||||||
{
|
|
||||||
return socket_ != INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
::closesocket(socket_);
|
|
||||||
socket_ = INVALID_SOCKET;
|
|
||||||
WSACleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKET fd() const
|
|
||||||
{
|
|
||||||
return socket_;
|
|
||||||
}
|
|
||||||
|
|
||||||
~tcp_client()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to connect or throw on failure
|
|
||||||
void connect(const std::string &host, int port)
|
|
||||||
{
|
|
||||||
// initialize winsock if needed
|
|
||||||
if (!winsock_initialized_())
|
|
||||||
{
|
|
||||||
init_winsock_();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_connected())
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
struct addrinfo hints
|
|
||||||
{};
|
|
||||||
ZeroMemory(&hints, sizeof(hints));
|
|
||||||
|
|
||||||
hints.ai_family = AF_INET; // IPv4
|
|
||||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
|
||||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
|
||||||
hints.ai_protocol = 0;
|
|
||||||
|
|
||||||
auto port_str = std::to_string(port);
|
|
||||||
struct addrinfo *addrinfo_result;
|
|
||||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
|
||||||
int last_error = 0;
|
|
||||||
if (rv != 0)
|
|
||||||
{
|
|
||||||
last_error = ::WSAGetLastError();
|
|
||||||
WSACleanup();
|
|
||||||
throw_winsock_error_("getaddrinfo failed", last_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try each address until we successfully connect(2).
|
|
||||||
|
|
||||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
|
||||||
{
|
|
||||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
|
||||||
if (socket_ == INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
last_error = ::WSAGetLastError();
|
|
||||||
WSACleanup();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
last_error = ::WSAGetLastError();
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
::freeaddrinfo(addrinfo_result);
|
|
||||||
if (socket_ == INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
WSACleanup();
|
|
||||||
throw_winsock_error_("connect failed", last_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set TCP_NODELAY
|
|
||||||
int enable_flag = 1;
|
|
||||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send exactly n_bytes of the given data.
|
|
||||||
// On error close the connection and throw.
|
|
||||||
void send(const char *data, size_t n_bytes)
|
|
||||||
{
|
|
||||||
size_t bytes_sent = 0;
|
|
||||||
while (bytes_sent < n_bytes)
|
|
||||||
{
|
|
||||||
const int send_flags = 0;
|
|
||||||
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
|
||||||
if (write_result == SOCKET_ERROR)
|
|
||||||
{
|
|
||||||
int last_error = ::WSAGetLastError();
|
|
||||||
close();
|
|
||||||
throw_winsock_error_("send failed", last_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_result == 0) // (probably should not happen but in any case..)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bytes_sent += static_cast<size_t>(write_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,145 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#error include tcp_client-windows.h instead
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// tcp client helper
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
class tcp_client
|
|
||||||
{
|
|
||||||
int socket_ = -1;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool is_connected() const
|
|
||||||
{
|
|
||||||
return socket_ != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
if (is_connected())
|
|
||||||
{
|
|
||||||
::close(socket_);
|
|
||||||
socket_ = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int fd() const
|
|
||||||
{
|
|
||||||
return socket_;
|
|
||||||
}
|
|
||||||
|
|
||||||
~tcp_client()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to connect or throw on failure
|
|
||||||
void connect(const std::string &host, int port)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
struct addrinfo hints
|
|
||||||
{};
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
|
||||||
hints.ai_family = AF_INET; // IPv4
|
|
||||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
|
||||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
|
||||||
hints.ai_protocol = 0;
|
|
||||||
|
|
||||||
auto port_str = std::to_string(port);
|
|
||||||
struct addrinfo *addrinfo_result;
|
|
||||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
|
||||||
if (rv != 0)
|
|
||||||
{
|
|
||||||
auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
|
|
||||||
throw_spdlog_ex(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try each address until we successfully connect(2).
|
|
||||||
int last_errno = 0;
|
|
||||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
|
||||||
{
|
|
||||||
int const flags = SOCK_CLOEXEC;
|
|
||||||
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
|
||||||
if (socket_ == -1)
|
|
||||||
{
|
|
||||||
last_errno = errno;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
|
|
||||||
if (rv == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
last_errno = errno;
|
|
||||||
::close(socket_);
|
|
||||||
socket_ = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
::freeaddrinfo(addrinfo_result);
|
|
||||||
if (socket_ == -1)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("::connect failed", last_errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set TCP_NODELAY
|
|
||||||
int enable_flag = 1;
|
|
||||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
|
|
||||||
|
|
||||||
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
|
|
||||||
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
|
||||||
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, (char *)&enable_flag, sizeof(enable_flag));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
|
||||||
#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send exactly n_bytes of the given data.
|
|
||||||
// On error close the connection and throw.
|
|
||||||
void send(const char *data, size_t n_bytes)
|
|
||||||
{
|
|
||||||
size_t bytes_sent = 0;
|
|
||||||
while (bytes_sent < n_bytes)
|
|
||||||
{
|
|
||||||
#if defined(MSG_NOSIGNAL)
|
|
||||||
const int send_flags = MSG_NOSIGNAL;
|
|
||||||
#else
|
|
||||||
const int send_flags = 0;
|
|
||||||
#endif
|
|
||||||
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
|
|
||||||
if (write_result < 0)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
throw_spdlog_ex("write(2) failed", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_result == 0) // (probably should not happen but in any case..)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
bytes_sent += static_cast<size_t>(write_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,124 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/thread_pool.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
|
||||||
: q_(q_max_items)
|
|
||||||
{
|
|
||||||
if (threads_n == 0 || threads_n > 1000)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
|
||||||
"range is 1-1000)");
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < threads_n; i++)
|
|
||||||
{
|
|
||||||
threads_.emplace_back([this, on_thread_start] {
|
|
||||||
on_thread_start();
|
|
||||||
this->thread_pool::worker_loop_();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
|
||||||
: thread_pool(q_max_items, threads_n, [] {})
|
|
||||||
{}
|
|
||||||
|
|
||||||
// message all threads to terminate gracefully join them
|
|
||||||
SPDLOG_INLINE thread_pool::~thread_pool()
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < threads_.size(); i++)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads_)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_ALL() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
|
||||||
post_async_msg_(std::move(async_m), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
|
||||||
{
|
|
||||||
return q_.overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
if (overflow_policy == async_overflow_policy::block)
|
|
||||||
{
|
|
||||||
q_.enqueue(std::move(new_msg));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
q_.enqueue_nowait(std::move(new_msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::worker_loop_()
|
|
||||||
{
|
|
||||||
while (process_next_msg_()) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// process next message in the queue
|
|
||||||
// return true if this thread should still be active (while no terminate msg
|
|
||||||
// was received)
|
|
||||||
bool SPDLOG_INLINE thread_pool::process_next_msg_()
|
|
||||||
{
|
|
||||||
async_msg incoming_async_msg;
|
|
||||||
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
|
||||||
if (!dequeued)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (incoming_async_msg.msg_type)
|
|
||||||
{
|
|
||||||
case async_msg_type::log: {
|
|
||||||
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case async_msg_type::flush: {
|
|
||||||
incoming_async_msg.worker_ptr->backend_flush_();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case async_msg_type::terminate: {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@ -1,120 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
#include <spdlog/details/mpmc_blocking_q.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
class async_logger;
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
|
||||||
|
|
||||||
enum class async_msg_type
|
|
||||||
{
|
|
||||||
log,
|
|
||||||
flush,
|
|
||||||
terminate
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
// Async msg to move to/from the queue
|
|
||||||
// Movable only. should never be copied
|
|
||||||
struct async_msg : log_msg_buffer
|
|
||||||
{
|
|
||||||
async_msg_type msg_type{async_msg_type::log};
|
|
||||||
async_logger_ptr worker_ptr;
|
|
||||||
|
|
||||||
async_msg() = default;
|
|
||||||
~async_msg() = default;
|
|
||||||
|
|
||||||
// should only be moved in or out of the queue..
|
|
||||||
async_msg(const async_msg &) = delete;
|
|
||||||
|
|
||||||
// support for vs2013 move
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
|
||||||
async_msg(async_msg &&other)
|
|
||||||
: log_msg_buffer(std::move(other))
|
|
||||||
, msg_type(other.msg_type)
|
|
||||||
, worker_ptr(std::move(other.worker_ptr))
|
|
||||||
{}
|
|
||||||
|
|
||||||
async_msg &operator=(async_msg &&other)
|
|
||||||
{
|
|
||||||
*static_cast<log_msg_buffer *>(this) = std::move(other);
|
|
||||||
msg_type = other.msg_type;
|
|
||||||
worker_ptr = std::move(other.worker_ptr);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
|
||||||
async_msg(async_msg &&) = default;
|
|
||||||
async_msg &operator=(async_msg &&) = default;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// construct from log_msg with given type
|
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
|
||||||
: log_msg_buffer{m}
|
|
||||||
, msg_type{the_type}
|
|
||||||
, worker_ptr{std::move(worker)}
|
|
||||||
{}
|
|
||||||
|
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
|
||||||
: log_msg_buffer{}
|
|
||||||
, msg_type{the_type}
|
|
||||||
, worker_ptr{std::move(worker)}
|
|
||||||
{}
|
|
||||||
|
|
||||||
explicit async_msg(async_msg_type the_type)
|
|
||||||
: async_msg{nullptr, the_type}
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SPDLOG_API thread_pool
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using item_type = async_msg;
|
|
||||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
|
||||||
|
|
||||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
|
||||||
thread_pool(size_t q_max_items, size_t threads_n);
|
|
||||||
|
|
||||||
// message all threads to terminate gracefully join them
|
|
||||||
~thread_pool();
|
|
||||||
|
|
||||||
thread_pool(const thread_pool &) = delete;
|
|
||||||
thread_pool &operator=(thread_pool &&) = delete;
|
|
||||||
|
|
||||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
|
||||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
|
||||||
size_t overrun_counter();
|
|
||||||
|
|
||||||
private:
|
|
||||||
q_type q_;
|
|
||||||
|
|
||||||
std::vector<std::thread> threads_;
|
|
||||||
|
|
||||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
|
||||||
void worker_loop_();
|
|
||||||
|
|
||||||
// process next message in the queue
|
|
||||||
// return true if this thread should still be active (while no terminate msg
|
|
||||||
// was received)
|
|
||||||
bool process_next_msg_();
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "thread_pool-inl.h"
|
|
||||||
#endif
|
|
@ -1,11 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
|
||||||
#define NOMINMAX // prevent windows redefining min/max
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
@ -1,216 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Support for logging binary data as hex
|
|
||||||
// format flags:
|
|
||||||
// {:X} - print in uppercase.
|
|
||||||
// {:s} - don't separate each byte with space.
|
|
||||||
// {:p} - don't print the position on each line start.
|
|
||||||
// {:n} - don't split the output to lines.
|
|
||||||
// {:a} - show ASCII if :n is not set
|
|
||||||
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// std::vector<char> v(200, 0x0b);
|
|
||||||
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
|
||||||
// char buf[128];
|
|
||||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
|
||||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
class dump_info
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dump_info(It range_begin, It range_end, size_t size_per_line)
|
|
||||||
: begin_(range_begin)
|
|
||||||
, end_(range_end)
|
|
||||||
, size_per_line_(size_per_line)
|
|
||||||
{}
|
|
||||||
|
|
||||||
It begin() const
|
|
||||||
{
|
|
||||||
return begin_;
|
|
||||||
}
|
|
||||||
It end() const
|
|
||||||
{
|
|
||||||
return end_;
|
|
||||||
}
|
|
||||||
size_t size_per_line() const
|
|
||||||
{
|
|
||||||
return size_per_line_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
It begin_, end_;
|
|
||||||
size_t size_per_line_;
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
|
|
||||||
// create a dump_info that wraps the given container
|
|
||||||
template<typename Container>
|
|
||||||
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
|
|
||||||
{
|
|
||||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
|
||||||
using Iter = typename Container::const_iterator;
|
|
||||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create dump_info from ranges
|
|
||||||
template<typename It>
|
|
||||||
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
|
|
||||||
{
|
|
||||||
return details::dump_info<It>(range_begin, range_end, size_per_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct formatter<spdlog::details::dump_info<T>>
|
|
||||||
{
|
|
||||||
const char delimiter = ' ';
|
|
||||||
bool put_newlines = true;
|
|
||||||
bool put_delimiters = true;
|
|
||||||
bool use_uppercase = false;
|
|
||||||
bool put_positions = true; // position on start of each line
|
|
||||||
bool show_ascii = false;
|
|
||||||
|
|
||||||
// parse the format string flags
|
|
||||||
template<typename ParseContext>
|
|
||||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
|
||||||
{
|
|
||||||
auto it = ctx.begin();
|
|
||||||
while (it != ctx.end() && *it != '}')
|
|
||||||
{
|
|
||||||
switch (*it)
|
|
||||||
{
|
|
||||||
case 'X':
|
|
||||||
use_uppercase = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
put_delimiters = false;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
put_positions = false;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
put_newlines = false;
|
|
||||||
show_ascii = false;
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
if (put_newlines)
|
|
||||||
{
|
|
||||||
show_ascii = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
// format the given bytes range as hex
|
|
||||||
template<typename FormatContext, typename Container>
|
|
||||||
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
|
||||||
{
|
|
||||||
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
|
||||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
|
||||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
|
||||||
|
|
||||||
#if FMT_VERSION < 60000
|
|
||||||
auto inserter = ctx.begin();
|
|
||||||
#else
|
|
||||||
auto inserter = ctx.out();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
|
||||||
auto start_of_line = the_range.begin();
|
|
||||||
for (auto i = the_range.begin(); i != the_range.end(); i++)
|
|
||||||
{
|
|
||||||
auto ch = static_cast<unsigned char>(*i);
|
|
||||||
|
|
||||||
if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
|
|
||||||
{
|
|
||||||
if (show_ascii && i != the_range.begin())
|
|
||||||
{
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
for (auto j = start_of_line; j < i; j++)
|
|
||||||
{
|
|
||||||
auto pc = static_cast<unsigned char>(*j);
|
|
||||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
|
|
||||||
|
|
||||||
// put first byte without delimiter in front of it
|
|
||||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
|
||||||
*inserter++ = hex_chars[ch & 0x0f];
|
|
||||||
start_of_line = i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (put_delimiters)
|
|
||||||
{
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
|
||||||
*inserter++ = hex_chars[ch & 0x0f];
|
|
||||||
}
|
|
||||||
if (show_ascii) // add ascii to last line
|
|
||||||
{
|
|
||||||
if (the_range.end() - the_range.begin() > size_per_line)
|
|
||||||
{
|
|
||||||
auto blank_num = size_per_line - (the_range.end() - start_of_line);
|
|
||||||
while (blank_num-- > 0)
|
|
||||||
{
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
if (put_delimiters)
|
|
||||||
{
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
*inserter++ = delimiter;
|
|
||||||
for (auto j = start_of_line; j != the_range.end(); j++)
|
|
||||||
{
|
|
||||||
auto pc = static_cast<unsigned char>(*j);
|
|
||||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inserter;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put newline(and position header)
|
|
||||||
template<typename It>
|
|
||||||
void put_newline(It inserter, std::size_t pos)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
*inserter++ = '\r';
|
|
||||||
#endif
|
|
||||||
*inserter++ = '\n';
|
|
||||||
|
|
||||||
if (put_positions)
|
|
||||||
{
|
|
||||||
fmt::format_to(inserter, "{:<04X}: ", pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace fmt
|
|
@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
--- Optional exception to the license ---
|
|
||||||
|
|
||||||
As an exception, if, as a result of your compiling your source code, portions
|
|
||||||
of this Software are embedded into a machine-executable object form of such
|
|
||||||
source code, you may redistribute such embedded portions in such object form
|
|
||||||
without including the above copyright and permission notices.
|
|
File diff suppressed because it is too large
Load Diff
@ -1,568 +0,0 @@
|
|||||||
// Formatting library for C++ - color support
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_COLOR_H_
|
|
||||||
#define FMT_COLOR_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
enum class color : uint32_t {
|
|
||||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
|
||||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
|
||||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
|
||||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
|
||||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
|
||||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
|
||||||
black = 0x000000, // rgb(0,0,0)
|
|
||||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
|
||||||
blue = 0x0000FF, // rgb(0,0,255)
|
|
||||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
|
||||||
brown = 0xA52A2A, // rgb(165,42,42)
|
|
||||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
|
||||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
|
||||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
|
||||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
|
||||||
coral = 0xFF7F50, // rgb(255,127,80)
|
|
||||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
|
||||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
|
||||||
crimson = 0xDC143C, // rgb(220,20,60)
|
|
||||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
|
||||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
|
||||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
|
||||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
|
||||||
dark_green = 0x006400, // rgb(0,100,0)
|
|
||||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
|
||||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
|
||||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
|
||||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
|
||||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
|
||||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
|
||||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
|
||||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
|
||||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
|
||||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
|
||||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
|
||||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
|
||||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
|
||||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
|
||||||
dim_gray = 0x696969, // rgb(105,105,105)
|
|
||||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
|
||||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
|
||||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
|
||||||
forest_green = 0x228B22, // rgb(34,139,34)
|
|
||||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
|
||||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
|
||||||
gold = 0xFFD700, // rgb(255,215,0)
|
|
||||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
|
||||||
gray = 0x808080, // rgb(128,128,128)
|
|
||||||
green = 0x008000, // rgb(0,128,0)
|
|
||||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
|
||||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
|
||||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
|
||||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
|
||||||
indigo = 0x4B0082, // rgb(75,0,130)
|
|
||||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
|
||||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
|
||||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
|
||||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
|
||||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
|
||||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
|
||||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
|
||||||
light_coral = 0xF08080, // rgb(240,128,128)
|
|
||||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
|
||||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
|
||||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
|
||||||
light_green = 0x90EE90, // rgb(144,238,144)
|
|
||||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
|
||||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
|
||||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
|
||||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
|
||||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
|
||||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
|
||||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
|
||||||
lime = 0x00FF00, // rgb(0,255,0)
|
|
||||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
|
||||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
|
||||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
maroon = 0x800000, // rgb(128,0,0)
|
|
||||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
|
||||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
|
||||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
|
||||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
|
||||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
|
||||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
|
||||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
|
||||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
|
||||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
|
||||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
|
||||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
|
||||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
|
||||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
|
||||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
|
||||||
navy = 0x000080, // rgb(0,0,128)
|
|
||||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
|
||||||
olive = 0x808000, // rgb(128,128,0)
|
|
||||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
|
||||||
orange = 0xFFA500, // rgb(255,165,0)
|
|
||||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
|
||||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
|
||||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
|
||||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
|
||||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
|
||||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
|
||||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
|
||||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
|
||||||
peru = 0xCD853F, // rgb(205,133,63)
|
|
||||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
|
||||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
|
||||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
|
||||||
purple = 0x800080, // rgb(128,0,128)
|
|
||||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
|
||||||
red = 0xFF0000, // rgb(255,0,0)
|
|
||||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
|
||||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
|
||||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
|
||||||
salmon = 0xFA8072, // rgb(250,128,114)
|
|
||||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
|
||||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
|
||||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
|
||||||
sienna = 0xA0522D, // rgb(160,82,45)
|
|
||||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
|
||||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
|
||||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
|
||||||
slate_gray = 0x708090, // rgb(112,128,144)
|
|
||||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
|
||||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
|
||||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
|
||||||
tan = 0xD2B48C, // rgb(210,180,140)
|
|
||||||
teal = 0x008080, // rgb(0,128,128)
|
|
||||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
|
||||||
tomato = 0xFF6347, // rgb(255,99,71)
|
|
||||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
|
||||||
violet = 0xEE82EE, // rgb(238,130,238)
|
|
||||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
|
||||||
white = 0xFFFFFF, // rgb(255,255,255)
|
|
||||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
|
||||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
|
||||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
|
||||||
}; // enum class color
|
|
||||||
|
|
||||||
enum class terminal_color : uint8_t {
|
|
||||||
black = 30,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
yellow,
|
|
||||||
blue,
|
|
||||||
magenta,
|
|
||||||
cyan,
|
|
||||||
white,
|
|
||||||
bright_black = 90,
|
|
||||||
bright_red,
|
|
||||||
bright_green,
|
|
||||||
bright_yellow,
|
|
||||||
bright_blue,
|
|
||||||
bright_magenta,
|
|
||||||
bright_cyan,
|
|
||||||
bright_white
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class emphasis : uint8_t {
|
|
||||||
bold = 1,
|
|
||||||
italic = 1 << 1,
|
|
||||||
underline = 1 << 2,
|
|
||||||
strikethrough = 1 << 3
|
|
||||||
};
|
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
|
||||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
|
||||||
struct rgb {
|
|
||||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
|
||||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
|
||||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
|
||||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
|
||||||
FMT_CONSTEXPR rgb(color hex)
|
|
||||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
|
||||||
g((uint32_t(hex) >> 8) & 0xFF),
|
|
||||||
b(uint32_t(hex) & 0xFF) {}
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// color is a struct of either a rgb color or a terminal color.
|
|
||||||
struct color_type {
|
|
||||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
|
||||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
|
||||||
value{} {
|
|
||||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
|
||||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
|
||||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
|
||||||
value{} {
|
|
||||||
value.term_color = static_cast<uint8_t>(term_color);
|
|
||||||
}
|
|
||||||
bool is_rgb;
|
|
||||||
union color_union {
|
|
||||||
uint8_t term_color;
|
|
||||||
uint32_t rgb_color;
|
|
||||||
} value;
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// Experimental text formatting support.
|
|
||||||
class text_style {
|
|
||||||
public:
|
|
||||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
|
||||||
: set_foreground_color(),
|
|
||||||
set_background_color(),
|
|
||||||
ems(em) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
|
||||||
if (!set_foreground_color) {
|
|
||||||
set_foreground_color = rhs.set_foreground_color;
|
|
||||||
foreground_color = rhs.foreground_color;
|
|
||||||
} else if (rhs.set_foreground_color) {
|
|
||||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't OR a terminal color"));
|
|
||||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!set_background_color) {
|
|
||||||
set_background_color = rhs.set_background_color;
|
|
||||||
background_color = rhs.background_color;
|
|
||||||
} else if (rhs.set_background_color) {
|
|
||||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't OR a terminal color"));
|
|
||||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
|
||||||
static_cast<uint8_t>(rhs.ems));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
|
||||||
const text_style& rhs) {
|
|
||||||
return lhs |= rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
|
||||||
if (!set_foreground_color) {
|
|
||||||
set_foreground_color = rhs.set_foreground_color;
|
|
||||||
foreground_color = rhs.foreground_color;
|
|
||||||
} else if (rhs.set_foreground_color) {
|
|
||||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't AND a terminal color"));
|
|
||||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!set_background_color) {
|
|
||||||
set_background_color = rhs.set_background_color;
|
|
||||||
background_color = rhs.background_color;
|
|
||||||
} else if (rhs.set_background_color) {
|
|
||||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't AND a terminal color"));
|
|
||||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
|
||||||
static_cast<uint8_t>(rhs.ems));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
|
||||||
const text_style& rhs) {
|
|
||||||
return lhs &= rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
|
||||||
return set_foreground_color;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
|
||||||
return set_background_color;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
|
||||||
return static_cast<uint8_t>(ems) != 0;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
|
||||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
|
||||||
return foreground_color;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
|
||||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
|
||||||
return background_color;
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
|
||||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
|
||||||
return ems;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
|
||||||
internal::color_type text_color) FMT_NOEXCEPT
|
|
||||||
: set_foreground_color(),
|
|
||||||
set_background_color(),
|
|
||||||
ems() {
|
|
||||||
if (is_foreground) {
|
|
||||||
foreground_color = text_color;
|
|
||||||
set_foreground_color = true;
|
|
||||||
} else {
|
|
||||||
background_color = text_color;
|
|
||||||
set_background_color = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
|
||||||
FMT_NOEXCEPT;
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
|
||||||
FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
internal::color_type foreground_color;
|
|
||||||
internal::color_type background_color;
|
|
||||||
bool set_foreground_color;
|
|
||||||
bool set_background_color;
|
|
||||||
emphasis ems;
|
|
||||||
};
|
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
|
||||||
return text_style(/*is_foreground=*/true, foreground);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
|
||||||
return text_style(/*is_foreground=*/false, background);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
|
||||||
return text_style(lhs) | rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
template <typename Char> struct ansi_color_escape {
|
|
||||||
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
|
||||||
const char* esc) FMT_NOEXCEPT {
|
|
||||||
// If we have a terminal color, we need to output another escape code
|
|
||||||
// sequence.
|
|
||||||
if (!text_color.is_rgb) {
|
|
||||||
bool is_background = esc == internal::data::background_color;
|
|
||||||
uint32_t value = text_color.value.term_color;
|
|
||||||
// Background ASCII codes are the same as the foreground ones but with
|
|
||||||
// 10 more.
|
|
||||||
if (is_background) value += 10u;
|
|
||||||
|
|
||||||
std::size_t index = 0;
|
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
|
||||||
buffer[index++] = static_cast<Char>('[');
|
|
||||||
|
|
||||||
if (value >= 100u) {
|
|
||||||
buffer[index++] = static_cast<Char>('1');
|
|
||||||
value %= 100u;
|
|
||||||
}
|
|
||||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
|
||||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
|
||||||
|
|
||||||
buffer[index++] = static_cast<Char>('m');
|
|
||||||
buffer[index++] = static_cast<Char>('\0');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 7; i++) {
|
|
||||||
buffer[i] = static_cast<Char>(esc[i]);
|
|
||||||
}
|
|
||||||
rgb color(text_color.value.rgb_color);
|
|
||||||
to_esc(color.r, buffer + 7, ';');
|
|
||||||
to_esc(color.g, buffer + 11, ';');
|
|
||||||
to_esc(color.b, buffer + 15, 'm');
|
|
||||||
buffer[19] = static_cast<Char>(0);
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
|
||||||
uint8_t em_codes[4] = {};
|
|
||||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
|
||||||
em_codes[3] = 9;
|
|
||||||
|
|
||||||
std::size_t index = 0;
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
if (!em_codes[i]) continue;
|
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
|
||||||
buffer[index++] = static_cast<Char>('[');
|
|
||||||
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
|
||||||
buffer[index++] = static_cast<Char>('m');
|
|
||||||
}
|
|
||||||
buffer[index++] = static_cast<Char>(0);
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
|
||||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
|
||||||
return buffer + std::char_traits<Char>::length(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Char buffer[7u + 3u * 4u + 1u];
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
|
||||||
char delimiter) FMT_NOEXCEPT {
|
|
||||||
out[0] = static_cast<Char>('0' + c / 100);
|
|
||||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
|
||||||
out[2] = static_cast<Char>('0' + c % 10);
|
|
||||||
out[3] = static_cast<Char>(delimiter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
|
||||||
internal::color_type foreground) FMT_NOEXCEPT {
|
|
||||||
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
|
||||||
internal::color_type background) FMT_NOEXCEPT {
|
|
||||||
return ansi_color_escape<Char>(background, internal::data::background_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
|
||||||
return ansi_color_escape<Char>(em);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
|
||||||
std::fputs(chars, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
|
||||||
std::fputws(chars, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
|
||||||
fputs(internal::data::reset_color, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
|
||||||
fputs(internal::data::wreset_color, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
|
||||||
const char* begin = data::reset_color;
|
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
|
||||||
buffer.append(begin, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
|
||||||
bool has_style = false;
|
|
||||||
if (ts.has_emphasis()) {
|
|
||||||
has_style = true;
|
|
||||||
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
|
||||||
buf.append(emphasis.begin(), emphasis.end());
|
|
||||||
}
|
|
||||||
if (ts.has_foreground()) {
|
|
||||||
has_style = true;
|
|
||||||
auto foreground =
|
|
||||||
internal::make_foreground_color<Char>(ts.get_foreground());
|
|
||||||
buf.append(foreground.begin(), foreground.end());
|
|
||||||
}
|
|
||||||
if (ts.has_background()) {
|
|
||||||
has_style = true;
|
|
||||||
auto background =
|
|
||||||
internal::make_background_color<Char>(ts.get_background());
|
|
||||||
buf.append(background.begin(), background.end());
|
|
||||||
}
|
|
||||||
internal::vformat_to(buf, format_str, args);
|
|
||||||
if (has_style) internal::reset_color<Char>(buf);
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
|
||||||
basic_memory_buffer<Char> buf;
|
|
||||||
internal::vformat_to(buf, ts, to_string_view(format), args);
|
|
||||||
buf.push_back(Char(0));
|
|
||||||
internal::fputs(buf.data(), f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Formats a string and prints it to the specified file stream using ANSI
|
|
||||||
escape sequences to specify text formatting.
|
|
||||||
Example:
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
||||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
|
||||||
const Args&... args) {
|
|
||||||
internal::check_format_string<Args...>(format_str);
|
|
||||||
using context = buffer_context<char_t<S>>;
|
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
|
||||||
specify text formatting.
|
|
||||||
Example:
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
||||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
|
||||||
return print(stdout, ts, format_str, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> vformat(
|
|
||||||
const text_style& ts, const S& format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buf;
|
|
||||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
|
||||||
return fmt::to_string(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Formats arguments and returns the result as a string using ANSI
|
|
||||||
escape sequences to specify text formatting.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
#include <fmt/color.h>
|
|
||||||
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
|
||||||
"The answer is {}", 42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
|
||||||
const Args&... args) {
|
|
||||||
return vformat(ts, to_string_view(format_str),
|
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_COLOR_H_
|
|
@ -1,595 +0,0 @@
|
|||||||
// Formatting library for C++ - experimental format string compilation
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_COMPILE_H_
|
|
||||||
#define FMT_COMPILE_H_
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// Part of a compiled format string. It can be either literal text or a
|
|
||||||
// replacement field.
|
|
||||||
template <typename Char> struct format_part {
|
|
||||||
enum class kind { arg_index, arg_name, text, replacement };
|
|
||||||
|
|
||||||
struct replacement {
|
|
||||||
arg_ref<Char> arg_id;
|
|
||||||
dynamic_format_specs<Char> specs;
|
|
||||||
};
|
|
||||||
|
|
||||||
kind part_kind;
|
|
||||||
union value {
|
|
||||||
int arg_index;
|
|
||||||
basic_string_view<Char> str;
|
|
||||||
replacement repl;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
|
||||||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
|
||||||
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
|
||||||
} val;
|
|
||||||
// Position past the end of the argument id.
|
|
||||||
const Char* arg_id_end = nullptr;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
|
||||||
: part_kind(k), val(v) {}
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
|
||||||
return format_part(kind::arg_index, index);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
|
||||||
return format_part(kind::arg_name, name);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
|
||||||
return format_part(kind::text, text);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
|
||||||
return format_part(kind::replacement, repl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct part_counter {
|
|
||||||
unsigned num_parts = 0;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end) ++num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
|
|
||||||
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
|
|
||||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
// Find the matching brace.
|
|
||||||
unsigned brace_counter = 0;
|
|
||||||
for (; begin != end; ++begin) {
|
|
||||||
if (*begin == '{') {
|
|
||||||
++brace_counter;
|
|
||||||
} else if (*begin == '}') {
|
|
||||||
if (brace_counter == 0u) break;
|
|
||||||
--brace_counter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char*) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Counts the number of parts in a format string.
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
|
||||||
part_counter<Char> counter;
|
|
||||||
parse_format_string<true>(format_str, counter);
|
|
||||||
return counter.num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename PartHandler>
|
|
||||||
class format_string_compiler : public error_handler {
|
|
||||||
private:
|
|
||||||
using part = format_part<Char>;
|
|
||||||
|
|
||||||
PartHandler handler_;
|
|
||||||
part part_;
|
|
||||||
basic_string_view<Char> format_str_;
|
|
||||||
basic_format_parse_context<Char> parse_context_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler)
|
|
||||||
: handler_(handler),
|
|
||||||
format_str_(format_str),
|
|
||||||
parse_context_(format_str) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end)
|
|
||||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id() {
|
|
||||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id(int id) {
|
|
||||||
parse_context_.check_arg_id(id);
|
|
||||||
part_ = part::make_arg_index(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
|
||||||
part_ = part::make_arg_name(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
|
||||||
part_.arg_id_end = ptr;
|
|
||||||
handler_(part_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
auto repl = typename part::replacement();
|
|
||||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
|
||||||
repl.specs, parse_context_);
|
|
||||||
auto it = parse_format_specs(begin, end, handler);
|
|
||||||
if (*it != '}') on_error("missing '}' in format string");
|
|
||||||
repl.arg_id = part_.part_kind == part::kind::arg_index
|
|
||||||
? arg_ref<Char>(part_.val.arg_index)
|
|
||||||
: arg_ref<Char>(part_.val.str);
|
|
||||||
auto part = part::make_replacement(repl);
|
|
||||||
part.arg_id_end = begin;
|
|
||||||
handler_(part);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compiles a format string and invokes handler(part) for each parsed part.
|
|
||||||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
|
||||||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler) {
|
|
||||||
parse_format_string<IS_CONSTEXPR>(
|
|
||||||
format_str,
|
|
||||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Range, typename Context, typename Id>
|
|
||||||
void format_arg(
|
|
||||||
basic_format_parse_context<typename Range::value_type>& parse_ctx,
|
|
||||||
Context& ctx, Id arg_id) {
|
|
||||||
ctx.advance_to(
|
|
||||||
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
|
||||||
namespace cf {
|
|
||||||
template <typename Context, typename Range, typename CompiledFormat>
|
|
||||||
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
|
|
||||||
-> typename Context::iterator {
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
basic_format_parse_context<char_type> parse_ctx(
|
|
||||||
to_string_view(cf.format_str_));
|
|
||||||
Context ctx(out.begin(), args);
|
|
||||||
|
|
||||||
const auto& parts = cf.parts();
|
|
||||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
|
||||||
++part_it) {
|
|
||||||
const auto& part = *part_it;
|
|
||||||
const auto& value = part.val;
|
|
||||||
|
|
||||||
using format_part_t = format_part<char_type>;
|
|
||||||
switch (part.part_kind) {
|
|
||||||
case format_part_t::kind::text: {
|
|
||||||
const auto text = value.str;
|
|
||||||
auto output = ctx.out();
|
|
||||||
auto&& it = reserve(output, text.size());
|
|
||||||
it = std::copy_n(text.begin(), text.size(), it);
|
|
||||||
ctx.advance_to(output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_index:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_name:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
internal::format_arg<Range>(parse_ctx, ctx, value.str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::replacement: {
|
|
||||||
const auto& arg_id_value = value.repl.arg_id.val;
|
|
||||||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
|
||||||
? ctx.arg(arg_id_value.index)
|
|
||||||
: ctx.arg(arg_id_value.name);
|
|
||||||
|
|
||||||
auto specs = value.repl.specs;
|
|
||||||
|
|
||||||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
|
||||||
handle_dynamic_spec<precision_checker>(specs.precision,
|
|
||||||
specs.precision_ref, ctx);
|
|
||||||
|
|
||||||
error_handler h;
|
|
||||||
numeric_specs_checker<error_handler> checker(h, arg.type());
|
|
||||||
if (specs.align == align::numeric) checker.require_numeric_argument();
|
|
||||||
if (specs.sign != sign::none) checker.check_sign();
|
|
||||||
if (specs.alt) checker.require_numeric_argument();
|
|
||||||
if (specs.precision >= 0) checker.check_precision();
|
|
||||||
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
ctx.advance_to(
|
|
||||||
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
} // namespace cf
|
|
||||||
|
|
||||||
struct basic_compiled_format {};
|
|
||||||
|
|
||||||
template <typename S, typename = void>
|
|
||||||
struct compiled_format_base : basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
using parts_container = std::vector<internal::format_part<char_type>>;
|
|
||||||
|
|
||||||
parts_container compiled_parts;
|
|
||||||
|
|
||||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
|
||||||
compile_format_string<false>(format_str,
|
|
||||||
[this](const format_part<char_type>& part) {
|
|
||||||
compiled_parts.push_back(part);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts_container& parts() const { return compiled_parts; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N> struct format_part_array {
|
|
||||||
format_part<Char> data[N] = {};
|
|
||||||
FMT_CONSTEXPR format_part_array() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N>
|
|
||||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
|
||||||
basic_string_view<Char> format_str) {
|
|
||||||
format_part_array<Char, N> parts;
|
|
||||||
unsigned counter = 0;
|
|
||||||
// This is not a lambda for compatibility with older compilers.
|
|
||||||
struct {
|
|
||||||
format_part<Char>* parts;
|
|
||||||
unsigned* counter;
|
|
||||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
|
||||||
parts[(*counter)++] = part;
|
|
||||||
}
|
|
||||||
} collector{parts.data, &counter};
|
|
||||||
compile_format_string<true>(format_str, collector);
|
|
||||||
if (counter < N) {
|
|
||||||
parts.data[counter] =
|
|
||||||
format_part<Char>::make_text(basic_string_view<Char>());
|
|
||||||
}
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
|
||||||
return (a < b) ? b : a;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S>
|
|
||||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
|
||||||
: basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
|
||||||
|
|
||||||
// Workaround for old compilers. Format string compilation will not be
|
|
||||||
// performed there anyway.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
|
||||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
|
||||||
#else
|
|
||||||
static const unsigned num_format_parts = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using parts_container = format_part<char_type>[num_format_parts];
|
|
||||||
|
|
||||||
const parts_container& parts() const {
|
|
||||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
|
||||||
compile_to_parts<char_type, num_format_parts>(
|
|
||||||
internal::to_string_view(S()));
|
|
||||||
return compiled_parts.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
|
||||||
class compiled_format : private compiled_format_base<S> {
|
|
||||||
public:
|
|
||||||
using typename compiled_format_base<S>::char_type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
basic_string_view<char_type> format_str_;
|
|
||||||
|
|
||||||
template <typename Context, typename Range, typename CompiledFormat>
|
|
||||||
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) ->
|
|
||||||
typename Context::iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
compiled_format() = delete;
|
|
||||||
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
|
||||||
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
template <typename... Args> struct type_list {};
|
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
|
||||||
template <int N, typename T, typename... Args>
|
|
||||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
|
||||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
|
||||||
if constexpr (N == 0)
|
|
||||||
return first;
|
|
||||||
else
|
|
||||||
return get<N - 1>(rest...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int N, typename> struct get_type_impl;
|
|
||||||
|
|
||||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
|
||||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <int N, typename T>
|
|
||||||
using get_type = typename get_type_impl<N, T>::type;
|
|
||||||
|
|
||||||
template <typename T> struct is_compiled_format : std::false_type {};
|
|
||||||
|
|
||||||
template <typename Char> struct text {
|
|
||||||
basic_string_view<Char> data;
|
|
||||||
using char_type = Char;
|
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
|
||||||
OutputIt format(OutputIt out, const Args&...) const {
|
|
||||||
// TODO: reserve
|
|
||||||
return copy_str<Char>(data.begin(), data.end(), out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|
||||||
size_t size) {
|
|
||||||
return {{&s[pos], size}};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt, typename T,
|
|
||||||
std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
|
||||||
OutputIt format_default(OutputIt out, T value) {
|
|
||||||
// TODO: reserve
|
|
||||||
format_int fi(value);
|
|
||||||
return std::copy(fi.data(), fi.data() + fi.size(), out);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, double value) {
|
|
||||||
writer w(out);
|
|
||||||
w.write(value);
|
|
||||||
return w.out();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, Char value) {
|
|
||||||
*out++ = value;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
|
||||||
OutputIt format_default(OutputIt out, const Char* value) {
|
|
||||||
auto length = std::char_traits<Char>::length(value);
|
|
||||||
return copy_str<Char>(value, value + length, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
|
||||||
template <typename Char, typename T, int N> struct field {
|
|
||||||
using char_type = Char;
|
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
|
||||||
const T& arg = get<N>(args...);
|
|
||||||
return format_default<Char>(out, arg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename T, int N>
|
|
||||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename L, typename R> struct concat {
|
|
||||||
L lhs;
|
|
||||||
R rhs;
|
|
||||||
using char_type = typename L::char_type;
|
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
|
||||||
out = lhs.format(out, args...);
|
|
||||||
return rhs.format(out, args...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename L, typename R>
|
|
||||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename L, typename R>
|
|
||||||
constexpr concat<L, R> make_concat(L lhs, R rhs) {
|
|
||||||
return {lhs, rhs};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct unknown_format {};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
|
|
||||||
for (size_t size = str.size(); pos != size; ++pos) {
|
|
||||||
if (str[pos] == '{' || str[pos] == '}') break;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
|
||||||
constexpr auto compile_format_string(S format_str);
|
|
||||||
|
|
||||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
|
||||||
constexpr auto parse_tail(T head, S format_str) {
|
|
||||||
if constexpr (POS != to_string_view(format_str).size()) {
|
|
||||||
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
|
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
|
||||||
unknown_format>())
|
|
||||||
return tail;
|
|
||||||
else
|
|
||||||
return make_concat(head, tail);
|
|
||||||
} else {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
|
||||||
// or unknown_format() on unrecognized input.
|
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
|
||||||
constexpr auto compile_format_string(S format_str) {
|
|
||||||
using char_type = typename S::char_type;
|
|
||||||
constexpr basic_string_view<char_type> str = format_str;
|
|
||||||
if constexpr (str[POS] == '{') {
|
|
||||||
if (POS + 1 == str.size())
|
|
||||||
throw format_error("unmatched '{' in format string");
|
|
||||||
if constexpr (str[POS + 1] == '{') {
|
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
|
||||||
} else if constexpr (str[POS + 1] == '}') {
|
|
||||||
using type = get_type<ID, Args>;
|
|
||||||
if constexpr (std::is_same<type, int>::value) {
|
|
||||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
|
||||||
format_str);
|
|
||||||
} else {
|
|
||||||
return unknown_format();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return unknown_format();
|
|
||||||
}
|
|
||||||
} else if constexpr (str[POS] == '}') {
|
|
||||||
if (POS + 1 == str.size())
|
|
||||||
throw format_error("unmatched '}' in format string");
|
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
|
||||||
} else {
|
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
|
||||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
|
||||||
format_str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // __cpp_if_constexpr
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
# ifdef __cpp_if_constexpr
|
|
||||||
template <typename... Args, typename S,
|
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) {
|
|
||||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
|
||||||
if constexpr (str.size() == 0) {
|
|
||||||
return internal::make_text(str, 0, 0);
|
|
||||||
} else {
|
|
||||||
constexpr auto result =
|
|
||||||
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
|
|
||||||
format_str);
|
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
|
||||||
internal::unknown_format>()) {
|
|
||||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
} else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
|
||||||
typename Char = typename CompiledFormat::char_type,
|
|
||||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
cf.format(std::back_inserter(buffer), args...);
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
|
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|
||||||
const Args&... args) {
|
|
||||||
return cf.format(out, args...);
|
|
||||||
}
|
|
||||||
# else
|
|
||||||
template <typename... Args, typename S,
|
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
|
|
||||||
return internal::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
}
|
|
||||||
# endif // __cpp_if_constexpr
|
|
||||||
#endif // FMT_USE_CONSTEXPR
|
|
||||||
|
|
||||||
// Compiles the format string which must be a string literal.
|
|
||||||
template <typename... Args, typename Char, size_t N>
|
|
||||||
auto compile(const Char (&format_str)[N])
|
|
||||||
-> internal::compiled_format<const Char*, Args...> {
|
|
||||||
return internal::compiled_format<const Char*, Args...>(
|
|
||||||
basic_string_view<Char>(format_str, N - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
|
||||||
typename Char = typename CompiledFormat::char_type,
|
|
||||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
using range = buffer_range<Char>;
|
|
||||||
using context = buffer_context<Char>;
|
|
||||||
internal::cf::vformat_to<context>(range(buffer), cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|
||||||
const Args&... args) {
|
|
||||||
using char_type = typename CompiledFormat::char_type;
|
|
||||||
using range = internal::output_range<OutputIt, char_type>;
|
|
||||||
using context = format_context_t<OutputIt, char_type>;
|
|
||||||
return internal::cf::vformat_to<context>(range(out), cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
|
||||||
const CompiledFormat& cf,
|
|
||||||
const Args&... args) {
|
|
||||||
auto it =
|
|
||||||
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
|
||||||
return {it.base(), it.count()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
|
||||||
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
return format_to(internal::counting_iterator(), cf, args...).count();
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_COMPILE_H_
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,78 +0,0 @@
|
|||||||
// Formatting library for C++ - std::locale support
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_LOCALE_H_
|
|
||||||
#define FMT_LOCALE_H_
|
|
||||||
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
template <typename Char>
|
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
using range = buffer_range<Char>;
|
|
||||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
|
||||||
internal::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
internal::vformat_to(loc, buffer, format_str, args);
|
|
||||||
return fmt::to_string(buffer);
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, const S& format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
return internal::vformat(loc, to_string_view(format_str), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
return internal::vformat(
|
|
||||||
loc, to_string_view(format_str),
|
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
|
||||||
typename Char = enable_if_t<
|
|
||||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
|
||||||
inline OutputIt vformat_to(
|
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
|
||||||
using range = internal::output_range<OutputIt, Char>;
|
|
||||||
return vformat_to<arg_formatter<range>>(
|
|
||||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
|
||||||
internal::is_string<S>::value)>
|
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
internal::check_format_string<Args...>(format_str);
|
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
return vformat_to(out, loc, to_string_view(format_str),
|
|
||||||
basic_format_args<context>(as));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_LOCALE_H_
|
|
@ -1,166 +0,0 @@
|
|||||||
// Formatting library for C++ - std::ostream support
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_OSTREAM_H_
|
|
||||||
#define FMT_OSTREAM_H_
|
|
||||||
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
template <typename CHar> class basic_printf_parse_context;
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
|
||||||
private:
|
|
||||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
|
||||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
|
||||||
|
|
||||||
buffer<Char>& buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
|
||||||
private:
|
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
|
||||||
void_t<> operator<<(null<>);
|
|
||||||
void_t<> operator<<(const Char*);
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
|
||||||
!std::is_enum<T>::value)>
|
|
||||||
void_t<> operator<<(T);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
|
||||||
// std::ostream).
|
|
||||||
template <typename T, typename Char> class is_streamable {
|
|
||||||
private:
|
|
||||||
template <typename U>
|
|
||||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
|
||||||
<< std::declval<U>()),
|
|
||||||
void_t<>>::value>
|
|
||||||
test(int);
|
|
||||||
|
|
||||||
template <typename> static std::false_type test(...);
|
|
||||||
|
|
||||||
using result = decltype(test<T>(0));
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const bool value = result::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write the content of buf to os.
|
|
||||||
template <typename Char>
|
|
||||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
|
||||||
const Char* buf_data = buf.data();
|
|
||||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
|
||||||
unsigned_streamsize size = buf.size();
|
|
||||||
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
|
|
||||||
do {
|
|
||||||
unsigned_streamsize n = size <= max_size ? size : max_size;
|
|
||||||
os.write(buf_data, static_cast<std::streamsize>(n));
|
|
||||||
buf_data += n;
|
|
||||||
size -= n;
|
|
||||||
} while (size != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename T>
|
|
||||||
void format_value(buffer<Char>& buf, const T& value,
|
|
||||||
locale_ref loc = locale_ref()) {
|
|
||||||
formatbuf<Char> format_buf(buf);
|
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
|
||||||
#endif
|
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
||||||
output << value;
|
|
||||||
buf.resize(buf.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
|
||||||
template <typename T, typename Char>
|
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
|
||||||
: private formatter<basic_string_view<Char>, Char> {
|
|
||||||
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
|
||||||
}
|
|
||||||
template <typename ParseCtx,
|
|
||||||
FMT_ENABLE_IF(std::is_same<
|
|
||||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
|
||||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
|
||||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
|
||||||
-> OutputIt {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
format_value(buffer, value, ctx.locale());
|
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
|
||||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
|
||||||
}
|
|
||||||
template <typename OutputIt>
|
|
||||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
|
||||||
-> OutputIt {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
format_value(buffer, value, ctx.locale());
|
|
||||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
internal::vformat_to(buffer, format_str, args);
|
|
||||||
internal::write(os, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::print(cerr, "Don't {}!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
|
||||||
vprint(os, to_string_view(format_str),
|
|
||||||
internal::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_OSTREAM_H_
|
|
@ -1,2 +0,0 @@
|
|||||||
#include "os.h"
|
|
||||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
|
@ -1,726 +0,0 @@
|
|||||||
// Formatting library for C++ - legacy printf implementation
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_PRINTF_H_
|
|
||||||
#define FMT_PRINTF_H_
|
|
||||||
|
|
||||||
#include <algorithm> // std::max
|
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
|
|
||||||
#include "ostream.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
|
||||||
// signed and unsigned integers.
|
|
||||||
template <bool IsSigned> struct int_checker {
|
|
||||||
template <typename T> static bool fits_in_int(T value) {
|
|
||||||
unsigned max = max_value<int>();
|
|
||||||
return value <= max;
|
|
||||||
}
|
|
||||||
static bool fits_in_int(bool) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct int_checker<true> {
|
|
||||||
template <typename T> static bool fits_in_int(T value) {
|
|
||||||
return value >= (std::numeric_limits<int>::min)() &&
|
|
||||||
value <= max_value<int>();
|
|
||||||
}
|
|
||||||
static bool fits_in_int(int) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class printf_precision_handler {
|
|
||||||
public:
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
||||||
int operator()(T value) {
|
|
||||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
|
||||||
FMT_THROW(format_error("number is too big"));
|
|
||||||
return (std::max)(static_cast<int>(value), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
||||||
int operator()(T) {
|
|
||||||
FMT_THROW(format_error("precision is not integer"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// An argument visitor that returns true iff arg is a zero integer.
|
|
||||||
class is_zero_int {
|
|
||||||
public:
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
||||||
bool operator()(T value) {
|
|
||||||
return value == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
||||||
bool operator()(T) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
|
||||||
|
|
||||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
|
||||||
|
|
||||||
template <typename T, typename Context> class arg_converter {
|
|
||||||
private:
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
|
|
||||||
basic_format_arg<Context>& arg_;
|
|
||||||
char_type type_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
|
||||||
: arg_(arg), type_(type) {}
|
|
||||||
|
|
||||||
void operator()(bool value) {
|
|
||||||
if (type_ != 's') operator()<bool>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
|
||||||
void operator()(U value) {
|
|
||||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
|
||||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
|
||||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
|
||||||
// Extra casts are used to silence warnings.
|
|
||||||
if (is_signed) {
|
|
||||||
arg_ = internal::make_arg<Context>(
|
|
||||||
static_cast<int>(static_cast<target_type>(value)));
|
|
||||||
} else {
|
|
||||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
|
||||||
arg_ = internal::make_arg<Context>(
|
|
||||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_signed) {
|
|
||||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
|
||||||
// std::printf("%lld", -42); // prints "4294967254"
|
|
||||||
// but we don't have to do the same because it's a UB.
|
|
||||||
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
|
||||||
} else {
|
|
||||||
arg_ = internal::make_arg<Context>(
|
|
||||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
|
||||||
void operator()(U) {} // No conversion needed for non-integral types.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Converts an integer argument to T for printf, if T is an integral type.
|
|
||||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
|
||||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
|
||||||
// unsigned).
|
|
||||||
template <typename T, typename Context, typename Char>
|
|
||||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
|
||||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts an integer argument to char for printf.
|
|
||||||
template <typename Context> class char_converter {
|
|
||||||
private:
|
|
||||||
basic_format_arg<Context>& arg_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
||||||
void operator()(T value) {
|
|
||||||
arg_ = internal::make_arg<Context>(
|
|
||||||
static_cast<typename Context::char_type>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
||||||
void operator()(T) {} // No conversion needed for non-integral types.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
|
||||||
// left alignment if it is negative.
|
|
||||||
template <typename Char> class printf_width_handler {
|
|
||||||
private:
|
|
||||||
using format_specs = basic_format_specs<Char>;
|
|
||||||
|
|
||||||
format_specs& specs_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|
||||||
unsigned operator()(T value) {
|
|
||||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
|
||||||
if (internal::is_negative(value)) {
|
|
||||||
specs_.align = align::left;
|
|
||||||
width = 0 - width;
|
|
||||||
}
|
|
||||||
unsigned int_max = max_value<int>();
|
|
||||||
if (width > int_max) FMT_THROW(format_error("number is too big"));
|
|
||||||
return static_cast<unsigned>(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
|
||||||
unsigned operator()(T) {
|
|
||||||
FMT_THROW(format_error("width is not integer"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
|
||||||
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename Context>
|
|
||||||
internal::truncating_iterator<OutputIt> printf(
|
|
||||||
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
return Context(it, format, args).format();
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
using internal::printf; // For printing into memory_buffer.
|
|
||||||
|
|
||||||
template <typename Range> class printf_arg_formatter;
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
|
||||||
using basic_format_parse_context<Char>::basic_format_parse_context;
|
|
||||||
};
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
The ``printf`` argument formatter.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Range>
|
|
||||||
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
|
||||||
public:
|
|
||||||
using iterator = typename Range::iterator;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using char_type = typename Range::value_type;
|
|
||||||
using base = internal::arg_formatter_base<Range>;
|
|
||||||
using context_type = basic_printf_context<iterator, char_type>;
|
|
||||||
|
|
||||||
context_type& context_;
|
|
||||||
|
|
||||||
void write_null_pointer(char) {
|
|
||||||
this->specs()->type = 0;
|
|
||||||
this->write("(nil)");
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_null_pointer(wchar_t) {
|
|
||||||
this->specs()->type = 0;
|
|
||||||
this->write(L"(nil)");
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
using format_specs = typename base::format_specs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs an argument formatter object.
|
|
||||||
*buffer* is a reference to the output buffer and *specs* contains format
|
|
||||||
specifier information for standard argument types.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
|
||||||
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
|
|
||||||
iterator operator()(T value) {
|
|
||||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
|
||||||
// use std::is_same instead.
|
|
||||||
if (std::is_same<T, bool>::value) {
|
|
||||||
format_specs& fmt_specs = *this->specs();
|
|
||||||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
|
||||||
fmt_specs.type = 0;
|
|
||||||
this->write(value != 0);
|
|
||||||
} else if (std::is_same<T, char_type>::value) {
|
|
||||||
format_specs& fmt_specs = *this->specs();
|
|
||||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
|
||||||
return (*this)(static_cast<int>(value));
|
|
||||||
fmt_specs.sign = sign::none;
|
|
||||||
fmt_specs.alt = false;
|
|
||||||
fmt_specs.align = align::right;
|
|
||||||
return base::operator()(value);
|
|
||||||
} else {
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
|
||||||
return this->out();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
|
||||||
iterator operator()(T value) {
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a null-terminated C string. */
|
|
||||||
iterator operator()(const char* value) {
|
|
||||||
if (value)
|
|
||||||
base::operator()(value);
|
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write("(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a null-terminated wide C string. */
|
|
||||||
iterator operator()(const wchar_t* value) {
|
|
||||||
if (value)
|
|
||||||
base::operator()(value);
|
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write(L"(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator operator()(basic_string_view<char_type> value) {
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator operator()(monostate value) { return base::operator()(value); }
|
|
||||||
|
|
||||||
/** Formats a pointer. */
|
|
||||||
iterator operator()(const void* value) {
|
|
||||||
if (value) return base::operator()(value);
|
|
||||||
this->specs()->type = 0;
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
return this->out();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
|
||||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
|
||||||
handle.format(context_.parse_context(), context_);
|
|
||||||
return this->out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T> struct printf_formatter {
|
|
||||||
printf_formatter() = delete;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
||||||
internal::format_value(internal::get_container(ctx.out()), value);
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** This template formats data and writes the output to a writer. */
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
|
||||||
public:
|
|
||||||
/** The character type for the output. */
|
|
||||||
using char_type = Char;
|
|
||||||
using iterator = OutputIt;
|
|
||||||
using format_arg = basic_format_arg<basic_printf_context>;
|
|
||||||
using parse_context_type = basic_printf_parse_context<Char>;
|
|
||||||
template <typename T> using formatter_type = printf_formatter<T>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using format_specs = basic_format_specs<char_type>;
|
|
||||||
|
|
||||||
OutputIt out_;
|
|
||||||
basic_format_args<basic_printf_context> args_;
|
|
||||||
parse_context_type parse_ctx_;
|
|
||||||
|
|
||||||
static void parse_flags(format_specs& specs, const Char*& it,
|
|
||||||
const Char* end);
|
|
||||||
|
|
||||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
|
||||||
// argument.
|
|
||||||
format_arg get_arg(int arg_index = -1);
|
|
||||||
|
|
||||||
// Parses argument index, flags and width and returns the argument index.
|
|
||||||
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a ``printf_context`` object. References to the arguments and
|
|
||||||
the writer are stored in the context object so make sure they have
|
|
||||||
appropriate lifetimes.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
|
||||||
basic_format_args<basic_printf_context> args)
|
|
||||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
|
||||||
|
|
||||||
OutputIt out() { return out_; }
|
|
||||||
void advance_to(OutputIt it) { out_ = it; }
|
|
||||||
|
|
||||||
internal::locale_ref locale() { return {}; }
|
|
||||||
|
|
||||||
format_arg arg(int id) const { return args_.get(id); }
|
|
||||||
|
|
||||||
parse_context_type& parse_context() { return parse_ctx_; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
parse_ctx_.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the range. */
|
|
||||||
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
|
|
||||||
OutputIt format();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
|
||||||
const Char*& it,
|
|
||||||
const Char* end) {
|
|
||||||
for (; it != end; ++it) {
|
|
||||||
switch (*it) {
|
|
||||||
case '-':
|
|
||||||
specs.align = align::left;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
specs.sign = sign::plus;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
specs.fill[0] = '0';
|
|
||||||
break;
|
|
||||||
case ' ':
|
|
||||||
specs.sign = sign::space;
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
specs.alt = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
|
||||||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
|
||||||
if (arg_index < 0)
|
|
||||||
arg_index = parse_ctx_.next_arg_id();
|
|
||||||
else
|
|
||||||
parse_ctx_.check_arg_id(--arg_index);
|
|
||||||
return internal::get_arg(*this, arg_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|
||||||
const Char* end,
|
|
||||||
format_specs& specs) {
|
|
||||||
int arg_index = -1;
|
|
||||||
char_type c = *it;
|
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
|
||||||
// preceded with '0' flag(s).
|
|
||||||
internal::error_handler eh;
|
|
||||||
int value = parse_nonnegative_int(it, end, eh);
|
|
||||||
if (it != end && *it == '$') { // value is an argument index
|
|
||||||
++it;
|
|
||||||
arg_index = value;
|
|
||||||
} else {
|
|
||||||
if (c == '0') specs.fill[0] = '0';
|
|
||||||
if (value != 0) {
|
|
||||||
// Nonzero value means that we parsed width and don't need to
|
|
||||||
// parse it or flags again, so return now.
|
|
||||||
specs.width = value;
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parse_flags(specs, it, end);
|
|
||||||
// Parse width.
|
|
||||||
if (it != end) {
|
|
||||||
if (*it >= '0' && *it <= '9') {
|
|
||||||
internal::error_handler eh;
|
|
||||||
specs.width = parse_nonnegative_int(it, end, eh);
|
|
||||||
} else if (*it == '*') {
|
|
||||||
++it;
|
|
||||||
specs.width = static_cast<int>(visit_format_arg(
|
|
||||||
internal::printf_width_handler<char_type>(specs), get_arg()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
template <typename ArgFormatter>
|
|
||||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|
||||||
auto out = this->out();
|
|
||||||
const Char* start = parse_ctx_.begin();
|
|
||||||
const Char* end = parse_ctx_.end();
|
|
||||||
auto it = start;
|
|
||||||
while (it != end) {
|
|
||||||
char_type c = *it++;
|
|
||||||
if (c != '%') continue;
|
|
||||||
if (it != end && *it == c) {
|
|
||||||
out = std::copy(start, it, out);
|
|
||||||
start = ++it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out = std::copy(start, it - 1, out);
|
|
||||||
|
|
||||||
format_specs specs;
|
|
||||||
specs.align = align::right;
|
|
||||||
|
|
||||||
// Parse argument index, flags and width.
|
|
||||||
int arg_index = parse_header(it, end, specs);
|
|
||||||
if (arg_index == 0) on_error("argument index out of range");
|
|
||||||
|
|
||||||
// Parse precision.
|
|
||||||
if (it != end && *it == '.') {
|
|
||||||
++it;
|
|
||||||
c = it != end ? *it : 0;
|
|
||||||
if ('0' <= c && c <= '9') {
|
|
||||||
internal::error_handler eh;
|
|
||||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
|
||||||
} else if (c == '*') {
|
|
||||||
++it;
|
|
||||||
specs.precision = static_cast<int>(
|
|
||||||
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
|
||||||
} else {
|
|
||||||
specs.precision = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
format_arg arg = get_arg(arg_index);
|
|
||||||
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
|
||||||
specs.alt = false;
|
|
||||||
if (specs.fill[0] == '0') {
|
|
||||||
if (arg.is_arithmetic())
|
|
||||||
specs.align = align::numeric;
|
|
||||||
else
|
|
||||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
|
||||||
c = it != end ? *it++ : 0;
|
|
||||||
char_type t = it != end ? *it : 0;
|
|
||||||
using internal::convert_arg;
|
|
||||||
switch (c) {
|
|
||||||
case 'h':
|
|
||||||
if (t == 'h') {
|
|
||||||
++it;
|
|
||||||
t = it != end ? *it : 0;
|
|
||||||
convert_arg<signed char>(arg, t);
|
|
||||||
} else {
|
|
||||||
convert_arg<short>(arg, t);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (t == 'l') {
|
|
||||||
++it;
|
|
||||||
t = it != end ? *it : 0;
|
|
||||||
convert_arg<long long>(arg, t);
|
|
||||||
} else {
|
|
||||||
convert_arg<long>(arg, t);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
convert_arg<intmax_t>(arg, t);
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
convert_arg<std::size_t>(arg, t);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
convert_arg<std::ptrdiff_t>(arg, t);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
// printf produces garbage when 'L' is omitted for long double, no
|
|
||||||
// need to do the same.
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--it;
|
|
||||||
convert_arg<void>(arg, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse type.
|
|
||||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
|
||||||
specs.type = static_cast<char>(*it++);
|
|
||||||
if (arg.is_integral()) {
|
|
||||||
// Normalize type.
|
|
||||||
switch (specs.type) {
|
|
||||||
case 'i':
|
|
||||||
case 'u':
|
|
||||||
specs.type = 'd';
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
|
|
||||||
arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = it;
|
|
||||||
|
|
||||||
// Format argument.
|
|
||||||
visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
|
||||||
}
|
|
||||||
return std::copy(start, it, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
using basic_printf_context_t =
|
|
||||||
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
|
|
||||||
Char>;
|
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
|
||||||
|
|
||||||
using printf_args = basic_format_args<printf_context>;
|
|
||||||
using wprintf_args = basic_format_args<wprintf_context>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
|
||||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
|
||||||
const Args&... args) {
|
|
||||||
return {args...};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
|
||||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename... Args>
|
|
||||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
|
||||||
const Args&... args) {
|
|
||||||
return {args...};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> vsprintf(
|
|
||||||
const S& format,
|
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
printf(buffer, to_string_view(format), args);
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Formats arguments and returns the result as a string.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
|
||||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
|
||||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline int vfprintf(
|
|
||||||
std::FILE* f, const S& format,
|
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
printf(buffer, to_string_view(format), args);
|
|
||||||
std::size_t size = buffer.size();
|
|
||||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
|
||||||
? -1
|
|
||||||
: static_cast<int>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the file *f*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
|
||||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
|
||||||
return vfprintf(f, to_string_view(format),
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline int vprintf(
|
|
||||||
const S& format,
|
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
|
||||||
return vfprintf(stdout, to_string_view(format), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to ``stdout``.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args,
|
|
||||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
|
||||||
inline int printf(const S& format_str, const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<char_t<S>>;
|
|
||||||
return vprintf(to_string_view(format_str),
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline int vfprintf(
|
|
||||||
std::basic_ostream<Char>& os, const S& format,
|
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
printf(buffer, to_string_view(format), args);
|
|
||||||
internal::write(os, buffer);
|
|
||||||
return static_cast<int>(buffer.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats arguments and writes the output to the range. */
|
|
||||||
template <typename ArgFormatter, typename Char,
|
|
||||||
typename Context =
|
|
||||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
|
||||||
typename ArgFormatter::iterator vprintf(
|
|
||||||
internal::buffer<Char>& out, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<type_identity_t<Context>> args) {
|
|
||||||
typename ArgFormatter::iterator iter(out);
|
|
||||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(cerr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
|
||||||
const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
|
||||||
return vfprintf(os, to_string_view(format_str),
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_PRINTF_H_
|
|
@ -1,387 +0,0 @@
|
|||||||
// Formatting library for C++ - experimental range support
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
|
||||||
// All Rights Reserved
|
|
||||||
// {fmt} support for ranges, containers and types tuple interface.
|
|
||||||
|
|
||||||
#ifndef FMT_RANGES_H_
|
|
||||||
#define FMT_RANGES_H_
|
|
||||||
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
// output only up to N items from the range.
|
|
||||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
|
||||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
template <typename Char> struct formatting_base {
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
|
||||||
struct formatting_range : formatting_base<Char> {
|
|
||||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
|
||||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
|
||||||
// range.
|
|
||||||
Char prefix;
|
|
||||||
Char delimiter;
|
|
||||||
Char postfix;
|
|
||||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
|
||||||
struct formatting_tuple : formatting_base<Char> {
|
|
||||||
Char prefix;
|
|
||||||
Char delimiter;
|
|
||||||
Char postfix;
|
|
||||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
template <typename RangeT, typename OutputIterator>
|
|
||||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
|
||||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
|
||||||
*out++ = *it;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIterator>
|
|
||||||
OutputIterator copy(const char* str, OutputIterator out) {
|
|
||||||
while (*str) *out++ = *str++;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIterator>
|
|
||||||
OutputIterator copy(char ch, OutputIterator out) {
|
|
||||||
*out++ = ch;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true value if T has std::string interface, like std::string_view.
|
|
||||||
template <typename T> class is_like_std_string {
|
|
||||||
template <typename U>
|
|
||||||
static auto check(U* p)
|
|
||||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
|
||||||
template <typename> static void check(...);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
|
||||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
|
||||||
|
|
||||||
template <typename... Ts> struct conditional_helper {};
|
|
||||||
|
|
||||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
|
||||||
|
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
|
||||||
template <typename T>
|
|
||||||
struct is_range_<
|
|
||||||
T, conditional_t<false,
|
|
||||||
conditional_helper<decltype(std::declval<T>().begin()),
|
|
||||||
decltype(std::declval<T>().end())>,
|
|
||||||
void>> : std::true_type {};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
|
||||||
template <typename T> class is_tuple_like_ {
|
|
||||||
template <typename U>
|
|
||||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
|
||||||
template <typename> static void check(...);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
|
||||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check for integer_sequence
|
|
||||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
|
||||||
template <typename T, T... N>
|
|
||||||
using integer_sequence = std::integer_sequence<T, N...>;
|
|
||||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
|
||||||
template <std::size_t N>
|
|
||||||
using make_index_sequence = std::make_index_sequence<N>;
|
|
||||||
#else
|
|
||||||
template <typename T, T... N> struct integer_sequence {
|
|
||||||
using value_type = T;
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <std::size_t... N>
|
|
||||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
|
||||||
|
|
||||||
template <typename T, std::size_t N, T... Ns>
|
|
||||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
|
||||||
template <typename T, T... Ns>
|
|
||||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
|
||||||
|
|
||||||
template <std::size_t N>
|
|
||||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <class Tuple, class F, size_t... Is>
|
|
||||||
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
|
||||||
using std::get;
|
|
||||||
// using free function get<I>(T) now.
|
|
||||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
|
||||||
(void)_; // blocks warnings
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
|
||||||
T const&) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
|
||||||
const auto indexes = get_indexes(tup);
|
|
||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
|
||||||
typename std::decay<Arg>::type>::value)>
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
|
||||||
return add_space ? " {}" : "{}";
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
|
||||||
typename std::decay<Arg>::type>::value)>
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
|
||||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
|
||||||
return add_space ? " '{}'" : "'{}'";
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
|
||||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TupleT, typename Char>
|
|
||||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
|
||||||
private:
|
|
||||||
// C++11 generic lambda for format()
|
|
||||||
template <typename FormatContext> struct format_each {
|
|
||||||
template <typename T> void operator()(const T& v) {
|
|
||||||
if (i > 0) {
|
|
||||||
if (formatting.add_prepostfix_space) {
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
out = internal::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
internal::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), v),
|
|
||||||
v);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatting_tuple<Char>& formatting;
|
|
||||||
std::size_t& i;
|
|
||||||
typename std::add_lvalue_reference<decltype(
|
|
||||||
std::declval<FormatContext>().out())>::type out;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatting_tuple<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return formatting.parse(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext = format_context>
|
|
||||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
||||||
auto out = ctx.out();
|
|
||||||
std::size_t i = 0;
|
|
||||||
internal::copy(formatting.prefix, out);
|
|
||||||
|
|
||||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
|
||||||
if (formatting.add_prepostfix_space) {
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
internal::copy(formatting.postfix, out);
|
|
||||||
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
|
||||||
internal::is_range_<T>::value &&
|
|
||||||
!internal::is_like_std_string<T>::value &&
|
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
|
||||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
|
||||||
struct formatter<RangeT, Char,
|
|
||||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
|
||||||
formatting_range<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return formatting.parse(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
|
||||||
FormatContext& ctx) {
|
|
||||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
|
||||||
std::size_t i = 0;
|
|
||||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
|
||||||
if (i > 0) {
|
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
|
||||||
out = internal::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
internal::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
|
||||||
*it);
|
|
||||||
if (++i > formatting.range_length_limit) {
|
|
||||||
out = format_to(out, " ... <other elements>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
|
||||||
return internal::copy(formatting.postfix, out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
|
||||||
const std::tuple<T...>& tuple;
|
|
||||||
basic_string_view<Char> sep;
|
|
||||||
|
|
||||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
|
||||||
: tuple{t}, sep{s} {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename... T>
|
|
||||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
typename FormatContext::iterator format(
|
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
|
||||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <typename FormatContext, size_t... N>
|
|
||||||
typename FormatContext::iterator format(
|
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
|
||||||
internal::index_sequence<N...>) {
|
|
||||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
typename FormatContext::iterator format_args(
|
|
||||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
|
||||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
|
||||||
// can be replaced with a constexpr branch in the variadic overload.
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext, typename Arg, typename... Args>
|
|
||||||
typename FormatContext::iterator format_args(
|
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
|
||||||
const Arg& arg, const Args&... args) {
|
|
||||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
|
||||||
auto out = ctx.out();
|
|
||||||
out = base{}.format(arg, ctx);
|
|
||||||
if (sizeof...(Args) > 0) {
|
|
||||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
|
||||||
ctx.advance_to(out);
|
|
||||||
return format_args(value, ctx, args...);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
std::tuple<int, char> t = {1, 'a'};
|
|
||||||
fmt::print("{}", fmt::join(t, ", "));
|
|
||||||
// Output: "1, a"
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename... T>
|
|
||||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
|
||||||
string_view sep) {
|
|
||||||
return {tuple, sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T>
|
|
||||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
|
||||||
wstring_view sep) {
|
|
||||||
return {tuple, sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Returns an object that formats `initializer_list` with elements separated by
|
|
||||||
`sep`.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
|
||||||
// Output: "1, 2, 3"
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
|
||||||
std::initializer_list<T> list, string_view sep) {
|
|
||||||
return join(std::begin(list), std::end(list), sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
|
||||||
std::initializer_list<T> list, wstring_view sep) {
|
|
||||||
return join(std::begin(list), std::end(list), sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
|
@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016-2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Include a bundled header-only copy of fmtlib or an external one.
|
|
||||||
// By default spdlog include its own copy.
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
|
||||||
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
|
|
||||||
#define FMT_HEADER_ONLY
|
|
||||||
#endif
|
|
||||||
#ifndef FMT_USE_WINDOWS_H
|
|
||||||
#define FMT_USE_WINDOWS_H 0
|
|
||||||
#endif
|
|
||||||
#include <spdlog/fmt/bundled/core.h>
|
|
||||||
#include <spdlog/fmt/bundled/format.h>
|
|
||||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
|
||||||
#include <fmt/core.h>
|
|
||||||
#include <fmt/format.h>
|
|
||||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
//
|
|
||||||
// include bundled or external copy of fmtlib's ostream support
|
|
||||||
//
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
#define FMT_HEADER_ONLY
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#include <spdlog/fmt/bundled/ostream.h>
|
|
||||||
#else
|
|
||||||
#include <fmt/ostream.h>
|
|
||||||
#endif
|
|
@ -1,18 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~formatter() = default;
|
|
||||||
virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
|
|
||||||
virtual std::unique_ptr<formatter> clone() const = 0;
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
class logger;
|
|
||||||
class formatter;
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
class sink;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,253 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/logger.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#include <spdlog/details/backtracer.h>
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// public methods
|
|
||||||
SPDLOG_INLINE logger::logger(const logger &other)
|
|
||||||
: name_(other.name_)
|
|
||||||
, sinks_(other.sinks_)
|
|
||||||
, level_(other.level_.load(std::memory_order_relaxed))
|
|
||||||
, flush_level_(other.flush_level_.load(std::memory_order_relaxed))
|
|
||||||
, custom_err_handler_(other.custom_err_handler_)
|
|
||||||
, tracer_(other.tracer_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
|
|
||||||
sinks_(std::move(other.sinks_)),
|
|
||||||
level_(other.level_.load(std::memory_order_relaxed)),
|
|
||||||
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
|
|
||||||
custom_err_handler_(std::move(other.custom_err_handler_)),
|
|
||||||
tracer_(std::move(other.tracer_))
|
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
this->swap(other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
name_.swap(other.name_);
|
|
||||||
sinks_.swap(other.sinks_);
|
|
||||||
|
|
||||||
// swap level_
|
|
||||||
auto other_level = other.level_.load();
|
|
||||||
auto my_level = level_.exchange(other_level);
|
|
||||||
other.level_.store(my_level);
|
|
||||||
|
|
||||||
// swap flush level_
|
|
||||||
other_level = other.flush_level_.load();
|
|
||||||
my_level = flush_level_.exchange(other_level);
|
|
||||||
other.flush_level_.store(my_level);
|
|
||||||
|
|
||||||
custom_err_handler_.swap(other.custom_err_handler_);
|
|
||||||
std::swap(tracer_, other.tracer_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void swap(logger &a, logger &b)
|
|
||||||
{
|
|
||||||
a.swap(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE level::level_enum logger::level() const
|
|
||||||
{
|
|
||||||
return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const std::string &logger::name() const
|
|
||||||
{
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set formatting for the sinks in this logger.
|
|
||||||
// each sink will get a separate instance of the formatter object.
|
|
||||||
SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
|
|
||||||
{
|
|
||||||
for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
|
|
||||||
{
|
|
||||||
if (std::next(it) == sinks_.end())
|
|
||||||
{
|
|
||||||
// last element - we can be move it.
|
|
||||||
(*it)->set_formatter(std::move(f));
|
|
||||||
break; // to prevent clang-tidy warning
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(*it)->set_formatter(f->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
|
||||||
{
|
|
||||||
auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
|
|
||||||
set_formatter(std::move(new_formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new backtrace sink and move to it all our child sinks
|
|
||||||
SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
|
|
||||||
{
|
|
||||||
tracer_.enable(n_messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore orig sinks and level and delete the backtrace sink
|
|
||||||
SPDLOG_INLINE void logger::disable_backtrace()
|
|
||||||
{
|
|
||||||
tracer_.disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::dump_backtrace()
|
|
||||||
{
|
|
||||||
dump_backtrace_();
|
|
||||||
}
|
|
||||||
|
|
||||||
// flush functions
|
|
||||||
SPDLOG_INLINE void logger::flush()
|
|
||||||
{
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
flush_level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE level::level_enum logger::flush_level() const
|
|
||||||
{
|
|
||||||
return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
// sinks
|
|
||||||
SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// error handler
|
|
||||||
SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
|
|
||||||
{
|
|
||||||
custom_err_handler_ = std::move(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create new logger with same sinks and configuration.
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
|
|
||||||
{
|
|
||||||
auto cloned = std::make_shared<logger>(*this);
|
|
||||||
cloned->name_ = std::move(logger_name);
|
|
||||||
return cloned;
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected methods
|
|
||||||
SPDLOG_INLINE void logger::log_it_(const spdlog::details::log_msg &log_msg, bool log_enabled, bool traceback_enabled)
|
|
||||||
{
|
|
||||||
if (log_enabled)
|
|
||||||
{
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
if (traceback_enabled)
|
|
||||||
{
|
|
||||||
tracer_.push_back(log_msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_flush_(msg))
|
|
||||||
{
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::flush_()
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::dump_backtrace_()
|
|
||||||
{
|
|
||||||
using details::log_msg;
|
|
||||||
if (tracer_.enabled())
|
|
||||||
{
|
|
||||||
sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
|
|
||||||
tracer_.foreach_pop([this](const log_msg &msg) { this->sink_it_(msg); });
|
|
||||||
sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
|
||||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
|
||||||
{
|
|
||||||
if (custom_err_handler_)
|
|
||||||
{
|
|
||||||
custom_err_handler_(msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using std::chrono::system_clock;
|
|
||||||
static std::mutex mutex;
|
|
||||||
static std::chrono::system_clock::time_point last_report_time;
|
|
||||||
static size_t err_counter = 0;
|
|
||||||
std::lock_guard<std::mutex> lk{mutex};
|
|
||||||
auto now = system_clock::now();
|
|
||||||
err_counter++;
|
|
||||||
if (now - last_report_time < std::chrono::seconds(1))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
last_report_time = now;
|
|
||||||
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
|
||||||
char date_buf[64];
|
|
||||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
|
||||||
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,366 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Thread safe logger (except for set_error_handler())
|
|
||||||
// Has name, log level, vector of std::shared sink pointers and formatter
|
|
||||||
// Upon each log write the logger:
|
|
||||||
// 1. Checks if its log level is enough to log the message and if yes:
|
|
||||||
// 2. Call the underlying sinks to do the job.
|
|
||||||
// 3. Each sink use its own private copy of a formatter to format the message
|
|
||||||
// and send to its destination.
|
|
||||||
//
|
|
||||||
// The use of private formatter per sink provides the opportunity to cache some
|
|
||||||
// formatted data, and support for different format per sink.
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#include <spdlog/details/backtracer.h>
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#ifndef SPDLOG_NO_EXCEPTIONS
|
|
||||||
#define SPDLOG_LOGGER_CATCH() \
|
|
||||||
catch (const std::exception &ex) \
|
|
||||||
{ \
|
|
||||||
err_handler_(ex.what()); \
|
|
||||||
} \
|
|
||||||
catch (...) \
|
|
||||||
{ \
|
|
||||||
err_handler_("Unknown exception in logger"); \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define SPDLOG_LOGGER_CATCH()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
class SPDLOG_API logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// Empty logger
|
|
||||||
explicit logger(std::string name)
|
|
||||||
: name_(std::move(name))
|
|
||||||
, sinks_()
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Logger with range on sinks
|
|
||||||
template<typename It>
|
|
||||||
logger(std::string name, It begin, It end)
|
|
||||||
: name_(std::move(name))
|
|
||||||
, sinks_(begin, end)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Logger with single sink
|
|
||||||
logger(std::string name, sink_ptr single_sink)
|
|
||||||
: logger(std::move(name), {std::move(single_sink)})
|
|
||||||
{}
|
|
||||||
|
|
||||||
// Logger with sinks init list
|
|
||||||
logger(std::string name, sinks_init_list sinks)
|
|
||||||
: logger(std::move(name), sinks.begin(), sinks.end())
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual ~logger() = default;
|
|
||||||
|
|
||||||
logger(const logger &other);
|
|
||||||
logger(logger &&other) SPDLOG_NOEXCEPT;
|
|
||||||
logger &operator=(logger other) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
|
|
||||||
|
|
||||||
// FormatString is a type derived from fmt::compile_string
|
|
||||||
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log_(loc, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
|
||||||
template<typename... Args>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log_(loc, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void trace(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::trace, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void debug(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::debug, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void info(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::info, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void warn(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::warn, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void error(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::err, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void critical(const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::critical, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void log(level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// T can be statically converted to string_view and isn't a fmt::compile_string
|
|
||||||
template<class T, typename std::enable_if<
|
|
||||||
std::is_convertible<const T &, spdlog::string_view_t>::value && !fmt::is_compile_string<T>::value, int>::type = 0>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
log(loc, lvl, string_view_t{msg});
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
|
|
||||||
{
|
|
||||||
bool log_enabled = should_log(lvl);
|
|
||||||
bool traceback_enabled = tracer_.enabled();
|
|
||||||
if (!log_enabled && !traceback_enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::log_msg log_msg(log_time, loc, name_, lvl, msg);
|
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(source_loc loc, level::level_enum lvl, string_view_t msg)
|
|
||||||
{
|
|
||||||
bool log_enabled = should_log(lvl);
|
|
||||||
bool traceback_enabled = tracer_.enabled();
|
|
||||||
if (!log_enabled && !traceback_enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::log_msg log_msg(loc, name_, lvl, msg);
|
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(level::level_enum lvl, string_view_t msg)
|
|
||||||
{
|
|
||||||
log(source_loc{}, lvl, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// T cannot be statically converted to string_view or wstring_view
|
|
||||||
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
|
|
||||||
!is_convertible_to_wstring_view<const T &>::value,
|
|
||||||
int>::type = 0>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
log(loc, lvl, "{}", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void trace(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::trace, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void debug(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::debug, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void info(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::info, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void warn(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::warn, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void error(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::err, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void critical(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::critical, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
#ifndef _WIN32
|
|
||||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
|
||||||
#else
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
bool log_enabled = should_log(lvl);
|
|
||||||
bool traceback_enabled = tracer_.enabled();
|
|
||||||
if (!log_enabled && !traceback_enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
// format to wmemory_buffer and convert to utf8
|
|
||||||
fmt::wmemory_buffer wbuf;
|
|
||||||
fmt::format_to(wbuf, fmt, args...);
|
|
||||||
|
|
||||||
memory_buf_t buf;
|
|
||||||
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
|
||||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
|
|
||||||
// T can be statically converted to wstring_view
|
|
||||||
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, int>::type = 0>
|
|
||||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
bool log_enabled = should_log(lvl);
|
|
||||||
bool traceback_enabled = tracer_.enabled();
|
|
||||||
if (!log_enabled && !traceback_enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
memory_buf_t buf;
|
|
||||||
details::os::wstr_to_utf8buf(msg, buf);
|
|
||||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
#endif // _WIN32
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
// return true logging is enabled for the given level.
|
|
||||||
bool should_log(level::level_enum msg_level) const
|
|
||||||
{
|
|
||||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return true if backtrace logging is enabled.
|
|
||||||
bool should_backtrace() const
|
|
||||||
{
|
|
||||||
return tracer_.enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level);
|
|
||||||
|
|
||||||
level::level_enum level() const;
|
|
||||||
|
|
||||||
const std::string &name() const;
|
|
||||||
|
|
||||||
// set formatting for the sinks in this logger.
|
|
||||||
// each sink will get a separate instance of the formatter object.
|
|
||||||
void set_formatter(std::unique_ptr<formatter> f);
|
|
||||||
|
|
||||||
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
|
||||||
|
|
||||||
// backtrace support.
|
|
||||||
// efficiently store all debug/trace messages in a circular buffer until needed for debugging.
|
|
||||||
void enable_backtrace(size_t n_messages);
|
|
||||||
void disable_backtrace();
|
|
||||||
void dump_backtrace();
|
|
||||||
|
|
||||||
// flush functions
|
|
||||||
void flush();
|
|
||||||
void flush_on(level::level_enum log_level);
|
|
||||||
level::level_enum flush_level() const;
|
|
||||||
|
|
||||||
// sinks
|
|
||||||
const std::vector<sink_ptr> &sinks() const;
|
|
||||||
|
|
||||||
std::vector<sink_ptr> &sinks();
|
|
||||||
|
|
||||||
// error handler
|
|
||||||
void set_error_handler(err_handler);
|
|
||||||
|
|
||||||
// create new logger with same sinks and configuration.
|
|
||||||
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string name_;
|
|
||||||
std::vector<sink_ptr> sinks_;
|
|
||||||
spdlog::level_t level_{level::info};
|
|
||||||
spdlog::level_t flush_level_{level::off};
|
|
||||||
err_handler custom_err_handler_{nullptr};
|
|
||||||
details::backtracer tracer_;
|
|
||||||
|
|
||||||
// common implementation for after templated public api has been resolved
|
|
||||||
template<typename FormatString, typename... Args>
|
|
||||||
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
bool log_enabled = should_log(lvl);
|
|
||||||
bool traceback_enabled = tracer_.enabled();
|
|
||||||
if (!log_enabled && !traceback_enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
memory_buf_t buf;
|
|
||||||
fmt::format_to(buf, fmt, args...);
|
|
||||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
|
|
||||||
// log the given message (if the given log level is high enough),
|
|
||||||
// and save backtrace (if backtrace is enabled).
|
|
||||||
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
|
|
||||||
virtual void sink_it_(const details::log_msg &msg);
|
|
||||||
virtual void flush_();
|
|
||||||
void dump_backtrace_();
|
|
||||||
bool should_flush_(const details::log_msg &msg);
|
|
||||||
|
|
||||||
// handle errors during logging.
|
|
||||||
// default handler prints the error to stderr at max rate of 1 message/sec.
|
|
||||||
void err_handler_(const std::string &msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
void swap(logger &a, logger &b);
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "logger-inl.h"
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,126 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/formatter.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <ctime>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// padding information.
|
|
||||||
struct padding_info
|
|
||||||
{
|
|
||||||
enum pad_side
|
|
||||||
{
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
center
|
|
||||||
};
|
|
||||||
|
|
||||||
padding_info() = default;
|
|
||||||
padding_info(size_t width, padding_info::pad_side side, bool truncate)
|
|
||||||
: width_(width)
|
|
||||||
, side_(side)
|
|
||||||
, truncate_(truncate)
|
|
||||||
, enabled_(true)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool enabled() const
|
|
||||||
{
|
|
||||||
return enabled_;
|
|
||||||
}
|
|
||||||
size_t width_ = 0;
|
|
||||||
pad_side side_ = left;
|
|
||||||
bool truncate_ = false;
|
|
||||||
bool enabled_ = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SPDLOG_API flag_formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit flag_formatter(padding_info padinfo)
|
|
||||||
: padinfo_(padinfo)
|
|
||||||
{}
|
|
||||||
flag_formatter() = default;
|
|
||||||
virtual ~flag_formatter() = default;
|
|
||||||
virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
padding_info padinfo_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
|
|
||||||
class SPDLOG_API custom_flag_formatter : public details::flag_formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
|
|
||||||
|
|
||||||
void set_padding_info(details::padding_info padding)
|
|
||||||
{
|
|
||||||
flag_formatter::padinfo_ = padding;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SPDLOG_API pattern_formatter final : public formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
|
|
||||||
|
|
||||||
explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
|
|
||||||
std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
|
|
||||||
|
|
||||||
// use default pattern is not given
|
|
||||||
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
|
|
||||||
|
|
||||||
pattern_formatter(const pattern_formatter &other) = delete;
|
|
||||||
pattern_formatter &operator=(const pattern_formatter &other) = delete;
|
|
||||||
|
|
||||||
std::unique_ptr<formatter> clone() const override;
|
|
||||||
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
pattern_formatter &add_flag(char flag, const Args &... args)
|
|
||||||
{
|
|
||||||
custom_handlers_[flag] = details::make_unique<T>(args...);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
void set_pattern(std::string pattern);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string pattern_;
|
|
||||||
std::string eol_;
|
|
||||||
pattern_time_type pattern_time_type_;
|
|
||||||
std::tm cached_tm_;
|
|
||||||
std::chrono::seconds last_log_secs_;
|
|
||||||
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
|
||||||
custom_flags custom_handlers_;
|
|
||||||
|
|
||||||
std::tm get_time_(const details::log_msg &msg);
|
|
||||||
template<typename Padder>
|
|
||||||
void handle_flag_(char flag, details::padding_info padding);
|
|
||||||
|
|
||||||
// Extract given pad spec (e.g. %8X)
|
|
||||||
// Advance the given it pass the end of the padding spec found (if any)
|
|
||||||
// Return padding.
|
|
||||||
static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
|
|
||||||
|
|
||||||
void compile_pattern_(const std::string &pattern);
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "pattern_formatter-inl.h"
|
|
||||||
#endif
|
|
@ -1,119 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
|
|
||||||
#include <spdlog/details/fmt_helper.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_ANDROID_RETRIES)
|
|
||||||
#define SPDLOG_ANDROID_RETRIES 2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Android sink (logging using __android_log_write)
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class android_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
|
|
||||||
: tag_(std::move(tag))
|
|
||||||
, use_raw_msg_(use_raw_msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
const android_LogPriority priority = convert_to_android_(msg.level);
|
|
||||||
memory_buf_t formatted;
|
|
||||||
if (use_raw_msg_)
|
|
||||||
{
|
|
||||||
details::fmt_helper::append_string_view(msg.payload, formatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
}
|
|
||||||
formatted.push_back('\0');
|
|
||||||
const char *msg_output = formatted.data();
|
|
||||||
|
|
||||||
// See system/core/liblog/logger_write.c for explanation of return value
|
|
||||||
int ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
|
||||||
int retry_count = 0;
|
|
||||||
while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
|
|
||||||
{
|
|
||||||
details::os::sleep_for_millis(5);
|
|
||||||
ret = __android_log_write(priority, tag_.c_str(), msg_output);
|
|
||||||
retry_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("__android_log_write() failed", ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
|
|
||||||
{
|
|
||||||
switch (level)
|
|
||||||
{
|
|
||||||
case spdlog::level::trace:
|
|
||||||
return ANDROID_LOG_VERBOSE;
|
|
||||||
case spdlog::level::debug:
|
|
||||||
return ANDROID_LOG_DEBUG;
|
|
||||||
case spdlog::level::info:
|
|
||||||
return ANDROID_LOG_INFO;
|
|
||||||
case spdlog::level::warn:
|
|
||||||
return ANDROID_LOG_WARN;
|
|
||||||
case spdlog::level::err:
|
|
||||||
return ANDROID_LOG_ERROR;
|
|
||||||
case spdlog::level::critical:
|
|
||||||
return ANDROID_LOG_FATAL;
|
|
||||||
default:
|
|
||||||
return ANDROID_LOG_DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string tag_;
|
|
||||||
bool use_raw_msg_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using android_sink_mt = android_sink<std::mutex>;
|
|
||||||
using android_sink_st = android_sink<details::null_mutex>;
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// Create and register android syslog logger
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::android_sink_st>(logger_name, tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#endif // __ANDROID__
|
|
@ -1,143 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/ansicolor_sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode)
|
|
||||||
: target_file_(target_file)
|
|
||||||
, mutex_(ConsoleMutex::mutex())
|
|
||||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
|
||||||
|
|
||||||
{
|
|
||||||
set_color_mode(mode);
|
|
||||||
colors_[level::trace] = to_string_(white);
|
|
||||||
colors_[level::debug] = to_string_(cyan);
|
|
||||||
colors_[level::info] = to_string_(green);
|
|
||||||
colors_[level::warn] = to_string_(yellow_bold);
|
|
||||||
colors_[level::err] = to_string_(red_bold);
|
|
||||||
colors_[level::critical] = to_string_(bold_on_red);
|
|
||||||
colors_[level::off] = to_string_(reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
colors_[color_level] = to_string_(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
// Wrap the originally formatted message in color codes.
|
|
||||||
// If color is not supported in the terminal, log as is instead.
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
msg.color_range_start = 0;
|
|
||||||
msg.color_range_end = 0;
|
|
||||||
memory_buf_t formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
|
||||||
{
|
|
||||||
// before color range
|
|
||||||
print_range_(formatted, 0, msg.color_range_start);
|
|
||||||
// in color range
|
|
||||||
print_ccode_(colors_[msg.level]);
|
|
||||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
|
||||||
print_ccode_(reset);
|
|
||||||
// after color range
|
|
||||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
|
||||||
}
|
|
||||||
else // no color
|
|
||||||
{
|
|
||||||
print_range_(formatted, 0, formatted.size());
|
|
||||||
}
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
|
|
||||||
{
|
|
||||||
return should_do_colors_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
|
||||||
{
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case color_mode::always:
|
|
||||||
should_do_colors_ = true;
|
|
||||||
return;
|
|
||||||
case color_mode::automatic:
|
|
||||||
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
|
||||||
return;
|
|
||||||
case color_mode::never:
|
|
||||||
should_do_colors_ = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
|
|
||||||
{
|
|
||||||
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
|
||||||
{
|
|
||||||
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
|
|
||||||
{
|
|
||||||
return std::string(sv.data(), sv.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ansicolor_stdout_sink
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)
|
|
||||||
: ansicolor_sink<ConsoleMutex>(stdout, mode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// ansicolor_stderr_sink
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode)
|
|
||||||
: ansicolor_sink<ConsoleMutex>(stderr, mode)
|
|
||||||
{}
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,118 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/console_globals.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This sink prefixes the output with an ANSI escape sequence color code
|
|
||||||
* depending on the severity
|
|
||||||
* of the message.
|
|
||||||
* If no color terminal detected, omit the escape codes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class ansicolor_sink : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
|
||||||
ansicolor_sink(FILE *target_file, color_mode mode);
|
|
||||||
~ansicolor_sink() override = default;
|
|
||||||
|
|
||||||
ansicolor_sink(const ansicolor_sink &other) = delete;
|
|
||||||
ansicolor_sink(ansicolor_sink &&other) = delete;
|
|
||||||
|
|
||||||
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
|
|
||||||
ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
|
|
||||||
|
|
||||||
void set_color(level::level_enum color_level, string_view_t color);
|
|
||||||
void set_color_mode(color_mode mode);
|
|
||||||
bool should_color();
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) override;
|
|
||||||
void flush() override;
|
|
||||||
void set_pattern(const std::string &pattern) final;
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
|
||||||
|
|
||||||
// Formatting codes
|
|
||||||
const string_view_t reset = "\033[m";
|
|
||||||
const string_view_t bold = "\033[1m";
|
|
||||||
const string_view_t dark = "\033[2m";
|
|
||||||
const string_view_t underline = "\033[4m";
|
|
||||||
const string_view_t blink = "\033[5m";
|
|
||||||
const string_view_t reverse = "\033[7m";
|
|
||||||
const string_view_t concealed = "\033[8m";
|
|
||||||
const string_view_t clear_line = "\033[K";
|
|
||||||
|
|
||||||
// Foreground colors
|
|
||||||
const string_view_t black = "\033[30m";
|
|
||||||
const string_view_t red = "\033[31m";
|
|
||||||
const string_view_t green = "\033[32m";
|
|
||||||
const string_view_t yellow = "\033[33m";
|
|
||||||
const string_view_t blue = "\033[34m";
|
|
||||||
const string_view_t magenta = "\033[35m";
|
|
||||||
const string_view_t cyan = "\033[36m";
|
|
||||||
const string_view_t white = "\033[37m";
|
|
||||||
|
|
||||||
/// Background colors
|
|
||||||
const string_view_t on_black = "\033[40m";
|
|
||||||
const string_view_t on_red = "\033[41m";
|
|
||||||
const string_view_t on_green = "\033[42m";
|
|
||||||
const string_view_t on_yellow = "\033[43m";
|
|
||||||
const string_view_t on_blue = "\033[44m";
|
|
||||||
const string_view_t on_magenta = "\033[45m";
|
|
||||||
const string_view_t on_cyan = "\033[46m";
|
|
||||||
const string_view_t on_white = "\033[47m";
|
|
||||||
|
|
||||||
/// Bold colors
|
|
||||||
const string_view_t yellow_bold = "\033[33m\033[1m";
|
|
||||||
const string_view_t red_bold = "\033[31m\033[1m";
|
|
||||||
const string_view_t bold_on_red = "\033[1m\033[41m";
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE *target_file_;
|
|
||||||
mutex_t &mutex_;
|
|
||||||
bool should_do_colors_;
|
|
||||||
std::unique_ptr<spdlog::formatter> formatter_;
|
|
||||||
std::array<std::string, level::n_levels> colors_;
|
|
||||||
void print_ccode_(const string_view_t &color_code);
|
|
||||||
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
|
||||||
static std::string to_string_(const string_view_t &sv);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
|
|
||||||
};
|
|
||||||
|
|
||||||
using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
|
|
||||||
using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
|
|
||||||
|
|
||||||
using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
|
|
||||||
using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "ansicolor_sink-inl.h"
|
|
||||||
#endif
|
|
@ -1,63 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink()
|
|
||||||
: formatter_{details::make_unique<spdlog::pattern_formatter>()}
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter)
|
|
||||||
: formatter_{std::move(formatter)}
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
sink_it_(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
set_pattern_(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
set_formatter_(std::move(sink_formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
|
|
||||||
{
|
|
||||||
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
|
|
||||||
{
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
//
|
|
||||||
// base sink templated over a mutex (either dummy or real)
|
|
||||||
// concrete implementation should override the sink_it_() and flush_() methods.
|
|
||||||
// locking is taken care of in this class - no locking needed by the
|
|
||||||
// implementers..
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
template<typename Mutex>
|
|
||||||
class base_sink : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
base_sink();
|
|
||||||
explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
|
|
||||||
~base_sink() override = default;
|
|
||||||
|
|
||||||
base_sink(const base_sink &) = delete;
|
|
||||||
base_sink(base_sink &&) = delete;
|
|
||||||
|
|
||||||
base_sink &operator=(const base_sink &) = delete;
|
|
||||||
base_sink &operator=(base_sink &&) = delete;
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) final;
|
|
||||||
void flush() final;
|
|
||||||
void set_pattern(const std::string &pattern) final;
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// sink formatter
|
|
||||||
std::unique_ptr<spdlog::formatter> formatter_;
|
|
||||||
Mutex mutex_;
|
|
||||||
|
|
||||||
virtual void sink_it_(const details::log_msg &msg) = 0;
|
|
||||||
virtual void flush_() = 0;
|
|
||||||
virtual void set_pattern_(const std::string &pattern);
|
|
||||||
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
|
|
||||||
};
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "base_sink-inl.h"
|
|
||||||
#endif
|
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/basic_file_sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate)
|
|
||||||
{
|
|
||||||
file_helper_.open(filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
|
|
||||||
{
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,58 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* Trivial file sink with single file as target
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class basic_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit basic_file_sink(const filename_t &filename, bool truncate = false);
|
|
||||||
const filename_t &filename() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using basic_file_sink_mt = basic_file_sink<std::mutex>;
|
|
||||||
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "basic_file_sink-inl.h"
|
|
||||||
#endif
|
|
@ -1,204 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/details/circular_q.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <ctime>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
|
|
||||||
*/
|
|
||||||
struct daily_filename_calculator
|
|
||||||
{
|
|
||||||
// Create filename for the form basename.YYYY-MM-DD
|
|
||||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
|
||||||
{
|
|
||||||
filename_t basename, ext;
|
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
|
||||||
return fmt::format(
|
|
||||||
SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rotating file sink based on date.
|
|
||||||
* If truncate != false , the created file will be truncated.
|
|
||||||
* If max_files > 0, retain only the last max_files and delete previous.
|
|
||||||
*/
|
|
||||||
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
|
|
||||||
class daily_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// create daily file sink which rotates on given time
|
|
||||||
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0)
|
|
||||||
: base_filename_(std::move(base_filename))
|
|
||||||
, rotation_h_(rotation_hour)
|
|
||||||
, rotation_m_(rotation_minute)
|
|
||||||
, truncate_(truncate)
|
|
||||||
, max_files_(max_files)
|
|
||||||
, filenames_q_()
|
|
||||||
{
|
|
||||||
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto now = log_clock::now();
|
|
||||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
|
||||||
file_helper_.open(filename, truncate_);
|
|
||||||
rotation_tp_ = next_rotation_tp_();
|
|
||||||
|
|
||||||
if (max_files_ > 0)
|
|
||||||
{
|
|
||||||
init_filenames_q_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t filename()
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
auto time = msg.time;
|
|
||||||
bool should_rotate = time >= rotation_tp_;
|
|
||||||
if (should_rotate)
|
|
||||||
{
|
|
||||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
|
|
||||||
file_helper_.open(filename, truncate_);
|
|
||||||
rotation_tp_ = next_rotation_tp_();
|
|
||||||
}
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
|
|
||||||
// Do the cleaning only at the end because it might throw on failure.
|
|
||||||
if (should_rotate && max_files_ > 0)
|
|
||||||
{
|
|
||||||
delete_old_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init_filenames_q_()
|
|
||||||
{
|
|
||||||
using details::os::path_exists;
|
|
||||||
|
|
||||||
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
|
|
||||||
std::vector<filename_t> filenames;
|
|
||||||
auto now = log_clock::now();
|
|
||||||
while (filenames.size() < max_files_)
|
|
||||||
{
|
|
||||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
|
||||||
if (!path_exists(filename))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
filenames.emplace_back(filename);
|
|
||||||
now -= std::chrono::hours(24);
|
|
||||||
}
|
|
||||||
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
|
|
||||||
{
|
|
||||||
filenames_q_.push_back(std::move(*iter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tm now_tm(log_clock::time_point tp)
|
|
||||||
{
|
|
||||||
time_t tnow = log_clock::to_time_t(tp);
|
|
||||||
return spdlog::details::os::localtime(tnow);
|
|
||||||
}
|
|
||||||
|
|
||||||
log_clock::time_point next_rotation_tp_()
|
|
||||||
{
|
|
||||||
auto now = log_clock::now();
|
|
||||||
tm date = now_tm(now);
|
|
||||||
date.tm_hour = rotation_h_;
|
|
||||||
date.tm_min = rotation_m_;
|
|
||||||
date.tm_sec = 0;
|
|
||||||
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
|
|
||||||
if (rotation_time > now)
|
|
||||||
{
|
|
||||||
return rotation_time;
|
|
||||||
}
|
|
||||||
return {rotation_time + std::chrono::hours(24)};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the file N rotations ago.
|
|
||||||
// Throw spdlog_ex on failure to delete the old file.
|
|
||||||
void delete_old_()
|
|
||||||
{
|
|
||||||
using details::os::filename_to_str;
|
|
||||||
using details::os::remove_if_exists;
|
|
||||||
|
|
||||||
filename_t current_file = file_helper_.filename();
|
|
||||||
if (filenames_q_.full())
|
|
||||||
{
|
|
||||||
auto old_filename = std::move(filenames_q_.front());
|
|
||||||
filenames_q_.pop_front();
|
|
||||||
bool ok = remove_if_exists(old_filename) == 0;
|
|
||||||
if (!ok)
|
|
||||||
{
|
|
||||||
filenames_q_.push_back(std::move(current_file));
|
|
||||||
throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filenames_q_.push_back(std::move(current_file));
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t base_filename_;
|
|
||||||
int rotation_h_;
|
|
||||||
int rotation_m_;
|
|
||||||
log_clock::time_point rotation_tp_;
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
bool truncate_;
|
|
||||||
uint16_t max_files_;
|
|
||||||
details::circular_q<filename_t> filenames_q_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
|
||||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> daily_logger_mt(
|
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> daily_logger_st(
|
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,97 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "base_sink.h"
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// Distribution sink (mux). Stores a vector of sinks which get called when log
|
|
||||||
// is called
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
class dist_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
dist_sink() = default;
|
|
||||||
explicit dist_sink(std::vector<std::shared_ptr<sink>> sinks)
|
|
||||||
: sinks_(sinks)
|
|
||||||
{}
|
|
||||||
|
|
||||||
dist_sink(const dist_sink &) = delete;
|
|
||||||
dist_sink &operator=(const dist_sink &) = delete;
|
|
||||||
|
|
||||||
void add_sink(std::shared_ptr<sink> sink)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_.push_back(sink);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_sink(std::shared_ptr<sink> sink)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
sinks_ = std::move(sinks);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<sink>> &sinks()
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern_(const std::string &pattern) override
|
|
||||||
{
|
|
||||||
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
|
||||||
{
|
|
||||||
base_sink<Mutex>::formatter_ = std::move(sink_formatter);
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->set_formatter(base_sink<Mutex>::formatter_->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<std::shared_ptr<sink>> sinks_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dist_sink_mt = dist_sink<std::mutex>;
|
|
||||||
using dist_sink_st = dist_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,90 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "dist_sink.h"
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
// Duplicate message removal sink.
|
|
||||||
// Skip the message if previous one is identical and less than "max_skip_duration" have passed
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
//
|
|
||||||
// #include <spdlog/sinks/dup_filter_sink.h>
|
|
||||||
//
|
|
||||||
// int main() {
|
|
||||||
// auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
|
|
||||||
// dup_filter->add_sink(std::make_shared<stdout_color_sink_mt>());
|
|
||||||
// spdlog::logger l("logger", dup_filter);
|
|
||||||
// l.info("Hello");
|
|
||||||
// l.info("Hello");
|
|
||||||
// l.info("Hello");
|
|
||||||
// l.info("Different Hello");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Will produce:
|
|
||||||
// [2019-06-25 17:50:56.511] [logger] [info] Hello
|
|
||||||
// [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
|
|
||||||
// [2019-06-25 17:50:56.512] [logger] [info] Different Hello
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
template<typename Mutex>
|
|
||||||
class dup_filter_sink : public dist_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<class Rep, class Period>
|
|
||||||
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
|
|
||||||
: max_skip_duration_{max_skip_duration}
|
|
||||||
{}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::chrono::microseconds max_skip_duration_;
|
|
||||||
log_clock::time_point last_msg_time_;
|
|
||||||
std::string last_msg_payload_;
|
|
||||||
size_t skip_counter_ = 0;
|
|
||||||
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
bool filtered = filter_(msg);
|
|
||||||
if (!filtered)
|
|
||||||
{
|
|
||||||
skip_counter_ += 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// log the "skipped.." message
|
|
||||||
if (skip_counter_ > 0)
|
|
||||||
{
|
|
||||||
memory_buf_t buf;
|
|
||||||
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
|
||||||
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
|
||||||
dist_sink<Mutex>::sink_it_(skipped_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// log current message
|
|
||||||
dist_sink<Mutex>::sink_it_(msg);
|
|
||||||
last_msg_time_ = msg.time;
|
|
||||||
skip_counter_ = 0;
|
|
||||||
last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// return whether the log msg should be displayed (true) or skipped (false)
|
|
||||||
bool filter_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
auto filter_duration = msg.time - last_msg_time_;
|
|
||||||
return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
|
|
||||||
using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright(c) 2016 Alexander Dalshov.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
|
|
||||||
#include <spdlog/details/windows_include.h>
|
|
||||||
#include <winbase.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* MSVC sink (logging using OutputDebugStringA)
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class msvc_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit msvc_sink() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using msvc_sink_mt = msvc_sink<std::mutex>;
|
|
||||||
using msvc_sink_st = msvc_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
using windebug_sink_mt = msvc_sink_mt;
|
|
||||||
using windebug_sink_st = msvc_sink_st;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
class null_sink : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &) override {}
|
|
||||||
void flush_() override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
using null_sink_mt = null_sink<details::null_mutex>;
|
|
||||||
using null_sink_st = null_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto null_logger = Factory::template create<sinks::null_sink_mt>(logger_name);
|
|
||||||
null_logger->set_level(level::off);
|
|
||||||
return null_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> null_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto null_logger = Factory::template create<sinks::null_sink_st>(logger_name);
|
|
||||||
null_logger->set_level(level::off);
|
|
||||||
return null_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,50 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
template<typename Mutex>
|
|
||||||
class ostream_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ostream_sink(std::ostream &os, bool force_flush = false)
|
|
||||||
: ostream_(os)
|
|
||||||
, force_flush_(force_flush)
|
|
||||||
{}
|
|
||||||
ostream_sink(const ostream_sink &) = delete;
|
|
||||||
ostream_sink &operator=(const ostream_sink &) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
|
|
||||||
if (force_flush_)
|
|
||||||
{
|
|
||||||
ostream_.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
ostream_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream &ostream_;
|
|
||||||
bool force_flush_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ostream_sink_mt = ostream_sink<std::mutex>;
|
|
||||||
using ostream_sink_st = ostream_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,74 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
|
||||||
#include "spdlog/details/circular_q.h"
|
|
||||||
#include "spdlog/details/log_msg_buffer.h"
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
/*
|
|
||||||
* Ring buffer sink
|
|
||||||
*/
|
|
||||||
template<typename Mutex>
|
|
||||||
class ringbuffer_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ringbuffer_sink(size_t n_items)
|
|
||||||
: q_{n_items}
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
auto items_available = q_.size();
|
|
||||||
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
|
|
||||||
std::vector<details::log_msg_buffer> ret;
|
|
||||||
ret.reserve(n_items);
|
|
||||||
for (size_t i = (items_available - n_items); i < items_available; i++)
|
|
||||||
{
|
|
||||||
ret.push_back(q_.at(i));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> last_formatted(size_t lim = 0)
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
auto items_available = q_.size();
|
|
||||||
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
|
|
||||||
std::vector<std::string> ret;
|
|
||||||
ret.reserve(n_items);
|
|
||||||
for (size_t i = (items_available - n_items); i < items_available; i++)
|
|
||||||
{
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
|
|
||||||
ret.push_back(fmt::to_string(formatted));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override
|
|
||||||
{
|
|
||||||
q_.push_back(details::log_msg_buffer{msg});
|
|
||||||
}
|
|
||||||
void flush_() override {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
details::circular_q<details::log_msg_buffer> q_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ringbuffer_sink_mt = ringbuffer_sink<std::mutex>;
|
|
||||||
using ringbuffer_sink_st = ringbuffer_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@ -1,131 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/rotating_file_sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <ctime>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
|
|
||||||
filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open)
|
|
||||||
: base_filename_(std::move(base_filename))
|
|
||||||
, max_size_(max_size)
|
|
||||||
, max_files_(max_files)
|
|
||||||
{
|
|
||||||
file_helper_.open(calc_filename(base_filename_, 0));
|
|
||||||
current_size_ = file_helper_.size(); // expensive. called only once
|
|
||||||
if (rotate_on_open && current_size_ > 0)
|
|
||||||
{
|
|
||||||
rotate_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// calc filename according to index and file extension if exists.
|
|
||||||
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename, std::size_t index)
|
|
||||||
{
|
|
||||||
if (index == 0u)
|
|
||||||
{
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
filename_t basename, ext;
|
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
|
||||||
return fmt::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
|
||||||
return file_helper_.filename();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
memory_buf_t formatted;
|
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
|
||||||
current_size_ += formatted.size();
|
|
||||||
if (current_size_ > max_size_)
|
|
||||||
{
|
|
||||||
rotate_();
|
|
||||||
current_size_ = formatted.size();
|
|
||||||
}
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE void rotating_file_sink<Mutex>::flush_()
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rotate files:
|
|
||||||
// log.txt -> log.1.txt
|
|
||||||
// log.1.txt -> log.2.txt
|
|
||||||
// log.2.txt -> log.3.txt
|
|
||||||
// log.3.txt -> delete
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
|
|
||||||
{
|
|
||||||
using details::os::filename_to_str;
|
|
||||||
using details::os::path_exists;
|
|
||||||
file_helper_.close();
|
|
||||||
for (auto i = max_files_; i > 0; --i)
|
|
||||||
{
|
|
||||||
filename_t src = calc_filename(base_filename_, i - 1);
|
|
||||||
if (!path_exists(src))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
filename_t target = calc_filename(base_filename_, i);
|
|
||||||
|
|
||||||
if (!rename_file_(src, target))
|
|
||||||
{
|
|
||||||
// if failed try again after a small delay.
|
|
||||||
// this is a workaround to a windows issue, where very high rotation
|
|
||||||
// rates can cause the rename to fail with permission denied (because of antivirus?).
|
|
||||||
details::os::sleep_for_millis(100);
|
|
||||||
if (!rename_file_(src, target))
|
|
||||||
{
|
|
||||||
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
|
|
||||||
current_size_ = 0;
|
|
||||||
throw_spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_helper_.reopen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete the target if exists, and rename the src file to target
|
|
||||||
// return true on success, false otherwise.
|
|
||||||
template<typename Mutex>
|
|
||||||
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename)
|
|
||||||
{
|
|
||||||
// try to delete the target file in case it already exists.
|
|
||||||
(void)details::os::remove(target_filename);
|
|
||||||
return details::os::rename(src_filename, target_filename) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/sinks/base_sink.h>
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Rotating file sink based on size
|
|
||||||
//
|
|
||||||
template<typename Mutex>
|
|
||||||
class rotating_file_sink final : public base_sink<Mutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false);
|
|
||||||
static filename_t calc_filename(const filename_t &filename, std::size_t index);
|
|
||||||
filename_t filename();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Rotate files:
|
|
||||||
// log.txt -> log.1.txt
|
|
||||||
// log.1.txt -> log.2.txt
|
|
||||||
// log.2.txt -> log.3.txt
|
|
||||||
// log.3.txt -> delete
|
|
||||||
void rotate_();
|
|
||||||
|
|
||||||
// delete the target if exists, and rename the src file to target
|
|
||||||
// return true on success, false otherwise.
|
|
||||||
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
|
|
||||||
|
|
||||||
filename_t base_filename_;
|
|
||||||
std::size_t max_size_;
|
|
||||||
std::size_t max_files_;
|
|
||||||
std::size_t current_size_;
|
|
||||||
details::file_helper file_helper_;
|
|
||||||
};
|
|
||||||
|
|
||||||
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
|
|
||||||
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
//
|
|
||||||
// factory functions
|
|
||||||
//
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> rotating_logger_mt(
|
|
||||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
inline std::shared_ptr<logger> rotating_logger_st(
|
|
||||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "rotating_file_sink-inl.h"
|
|
||||||
#endif
|
|
@ -1,25 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const
|
|
||||||
{
|
|
||||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
level_.store(log_level, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#include <spdlog/formatter.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
class SPDLOG_API sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~sink() = default;
|
|
||||||
virtual void log(const details::log_msg &msg) = 0;
|
|
||||||
virtual void flush() = 0;
|
|
||||||
virtual void set_pattern(const std::string &pattern) = 0;
|
|
||||||
virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level);
|
|
||||||
level::level_enum level() const;
|
|
||||||
bool should_log(level::level_enum msg_level) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// sink log level - default is all
|
|
||||||
level_t level_{level::trace};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "sink-inl.h"
|
|
||||||
#endif
|
|
@ -1,38 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/logger.h>
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,45 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <spdlog/sinks/wincolor_sink.h>
|
|
||||||
#else
|
|
||||||
#include <spdlog/sinks/ansicolor_sink.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace sinks {
|
|
||||||
#ifdef _WIN32
|
|
||||||
using stdout_color_sink_mt = wincolor_stdout_sink_mt;
|
|
||||||
using stdout_color_sink_st = wincolor_stdout_sink_st;
|
|
||||||
using stderr_color_sink_mt = wincolor_stderr_sink_mt;
|
|
||||||
using stderr_color_sink_st = wincolor_stderr_sink_st;
|
|
||||||
#else
|
|
||||||
using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
|
|
||||||
using stdout_color_sink_st = ansicolor_stdout_sink_st;
|
|
||||||
using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
|
|
||||||
using stderr_color_sink_st = ansicolor_stderr_sink_st;
|
|
||||||
#endif
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic);
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "stdout_color_sinks-inl.h"
|
|
||||||
#endif
|
|
@ -1,94 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/sinks/stdout_sinks.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/console_globals.h>
|
|
||||||
#include <spdlog/pattern_formatter.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
|
||||||
: mutex_(ConsoleMutex::mutex())
|
|
||||||
, file_(file)
|
|
||||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
memory_buf_t formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
|
||||||
fflush(file_); // flush every line to terminal
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::flush()
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fflush(file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_pattern(const std::string &pattern)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// stdout sink
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE stdout_sink<ConsoleMutex>::stdout_sink()
|
|
||||||
: stdout_sink_base<ConsoleMutex>(stdout)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// stderr sink
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
SPDLOG_INLINE stderr_sink<ConsoleMutex>::stderr_sink()
|
|
||||||
: stdout_sink_base<ConsoleMutex>(stderr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// factory methods
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stdout_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_sink_mt>(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Factory>
|
|
||||||
SPDLOG_INLINE std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
return Factory::template create<sinks::stderr_sink_st>(logger_name);
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@ -1,80 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/console_globals.h>
|
|
||||||
#include <spdlog/details/synchronous_factory.h>
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace sinks {
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class stdout_sink_base : public sink
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
|
||||||
explicit stdout_sink_base(FILE *file);
|
|
||||||
~stdout_sink_base() override = default;
|
|
||||||
|
|
||||||
stdout_sink_base(const stdout_sink_base &other) = delete;
|
|
||||||
stdout_sink_base(stdout_sink_base &&other) = delete;
|
|
||||||
|
|
||||||
stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
|
|
||||||
stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
|
|
||||||
|
|
||||||
void log(const details::log_msg &msg) override;
|
|
||||||
void flush() override;
|
|
||||||
void set_pattern(const std::string &pattern) override;
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
mutex_t &mutex_;
|
|
||||||
FILE *file_;
|
|
||||||
std::unique_ptr<spdlog::formatter> formatter_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class stdout_sink : public stdout_sink_base<ConsoleMutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
stdout_sink();
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
|
||||||
class stderr_sink : public stdout_sink_base<ConsoleMutex>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
stderr_sink();
|
|
||||||
};
|
|
||||||
|
|
||||||
using stdout_sink_mt = stdout_sink<details::console_mutex>;
|
|
||||||
using stdout_sink_st = stdout_sink<details::console_nullmutex>;
|
|
||||||
|
|
||||||
using stderr_sink_mt = stderr_sink<details::console_mutex>;
|
|
||||||
using stderr_sink_st = stderr_sink<details::console_nullmutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
|
||||||
|
|
||||||
// factory methods
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
|
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
|
||||||
std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "stdout_sinks-inl.h"
|
|
||||||
#endif
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user