If the log file is not available, then output the log message to the console. Any improvement or suggestion for this implementation for the logger by the template? Any potiencial problem that I should be aware of?
#ifndef _LOGGER_HEADER__
#define _LOGGER_HEADER__
#include <iostream>
#include <string>
#include <sstream>
#include <chrono>
#include <atomic>
#include <mutex>
#include <fstream>
#include <thread>
#include <vector>
#include <iomanip>
namespace Logger
{
enum LOG_LEVEL
{
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG,
LOG_LEVEL_ERROR,
};
enum LOG_MEDIA
{
CONSOLE,
LOG_FILE,
};
extern std::atomic<bool> is_enable_log;
extern bool is_log_file_ready;
extern std::mutex log_mutex;
extern enum LOG_MEDIA log_media;
extern std::ofstream log_stream;
std::string GetCurTime();
void Log(std::stringstream &ss, const char* format);
template<typename T, typename... Targs>
void Log(std::stringstream &ss, const char* format, T value, Targs... Fargs)
{
for (; *format != '\0'; format++)
{
if (('%' == *format) && ('#' == *(format+1)))
{
ss << value;
Log(ss, format + 2, Fargs...); // recursive call
return;
}
ss << *format;
}
}
template<typename... Targs>
void Log(LOG_LEVEL level, const char* file, const int line, const char* function, const char* format, Targs... Fargs)
{
if (!Logger::is_enable_log.load())
{
return;
}
std::stringstream ss;
std::string file_path(file);
auto loc = file_path.find_last_of("/\\");
ss << Logger::GetCurTime() << " "
<< std::setw(11) << file_path.substr(loc+1)
<< "[" << std::setw(4) << line << " | " << std::this_thread::get_id() <<"] <"
<< std::setw(25) << function << "()> "; // "
Log(ss, format, Fargs...);
DOWNGRADE_TO_COUT:
if (Logger::LOG_FILE == Logger::log_media)
{
if (!is_log_file_ready)
{
try
{
std::lock_guard<std::mutex> guard(Logger::log_mutex);
if (!is_log_file_ready)
{
Logger::log_stream.open("sdk.log", std::ofstream::out | std::ofstream::trunc);
is_log_file_ready = true;
}
}
catch (const std::exception& ex)
{
std::lock_guard<std::mutex> guard(Logger::log_mutex);
std::cout << "encounter exception when open the file to write" << ex.what() << std::endl;
Logger::log_media = Logger::CONSOLE;
goto DOWNGRADE_TO_COUT;
}
}
std::lock_guard<std::mutex> guard(Logger::log_mutex);
Logger::log_stream << ss.str()<<std::endl;
Logger::log_stream.flush();
}
else
{
std::lock_guard<std::mutex> guard(Logger::log_mutex);
std::cout << "[SDK] " << ss.str();
}
}
void LogDestory();
}
#if WIN32
#define LOG_INFO(format, ...) Logger::Log(Logger::LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, format, __VA_ARGS__)
#define LOG_DEBUG(format, ...) Logger::Log(Logger::LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, format, __VA_ARGS__)
#define LOG_ERROR(format, ...) Logger::Log(Logger::LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, format, __VA_ARGS__)
#else
#define LOG_INFO(format, arg...) Logger::Log(Logger::LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, format, ##arg)
#define LOG_DEBUG(format, arg...) Logger::Log(Logger::LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, format, ##arg)
#define LOG_ERROR(format, arg...) Logger::Log(Logger::LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, format, ##arg)
#endif
#define LOG_INFO_ENTER_FUNCTION LOG_INFO("enter function:%#", __FUNCTION__)
#define LOG_INFO_LEAVE_FUNCTION LOG_INFO("leave function:%#", __FUNCTION__)
#endif
#include <iomanip>
#include <ctime>
std::atomic<bool> Logger::is_enable_log{ true };
bool Logger::is_log_file_ready{ false };
std::mutex Logger::log_mutex;
enum Logger::LOG_MEDIA Logger::log_media = Logger::LOG_MEDIA::LOG_FILE;
std::ofstream Logger::log_stream;
void Logger::LogDestory()
{
Logger::is_log_file_ready = false;
Logger::is_enable_log.store(false);
Logger::log_stream.close();
}
void Logger::Log(std::stringstream &ss, const char* format)
{
ss << format;
}
std::string Logger::GetCurTime()
{
std::stringstream time_stream;
std::chrono::time_point<std::chrono::system_clock> current_time_point = std::chrono::system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(current_time_point);
std::time_t t = std::time(&tnow); // get time now
std::tm* now = std::localtime(&t);
time_stream << (now->tm_year + 1900) << '-'
<< (now->tm_mon + 1) << '-'
<< now->tm_mday << " "
<< now->tm_hour << ":"
<< std::setw(2) << now->tm_min << ":"
<< std::setw(2) << now->tm_sec << ".";
auto current_without_msec = std::chrono::system_clock::from_time_t(std::mktime(now));
time_stream << std::setw(3) << std::chrono::duration_cast<std::chrono::milliseconds>(current_time_point - current_without_msec).count();
return time_stream.str();
}
void TestLogger()
{
std::cout << "thread speaking" << std::this_thread::get_id() << std::endl;
for (int i = 0; i < 50; i++)
{
std::string str = "test";
bool flag = true;
LOG_INFO("%# world %# %# \n", str, 123, std::this_thread::get_id());
LOG_INFO("this is %# a test %# %# %#\n", 1.0, flag, 3, std::this_thread::get_id());
LOG_INFO("this is %# a test %# %# %#\n", "go out of my way", 'h', 3.1415926, std::this_thread::get_id());
}
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 3; i++)
{
threads.push_back(std::thread(TestLogger));
}
for (auto &thread : threads)
{
thread.join();
}
return 0;
}
```
std::source_location
needsc++20
support, which's not avaliable for my compiler(i.ec++17
at most). \$\endgroup\$