43 if (
const char * env_severity = getenv(
"NDMSPC_LOG_LEVEL")) {
46 std::cout <<
"NLogger: Setting log level to '" << env_severity <<
"' ... " << std::endl;
49 if (
const char * env_logdir = getenv(
"NDMSPC_LOG_DIR")) {
54 if (
const char * env_process_name = getenv(
"NDMSPC_PROCESS_NAME")) {
63 if (
const char * env_file_output = getenv(
"NDMSPC_LOG_FILE")) {
64 std::string value(env_file_output);
65 fgFileOutput = (value ==
"1" || value ==
"true" || value ==
"TRUE");
68 if (
const char * env_console_output = getenv(
"NDMSPC_LOG_CONSOLE")) {
69 std::string value(env_console_output);
70 if (value ==
"0" || value ==
"false" || value ==
"FALSE") {
73 else if (value ==
"1" || value ==
"true" || value ==
"TRUE") {
79 std::thread::id main_tid = std::this_thread::get_id();
80 std::string main_thread_name =
"main";
83 if (
const char * env_main_thread = getenv(
"NDMSPC_MAIN_THREAD_NAME")) {
84 main_thread_name = env_main_thread;
94 catch (
const std::exception & e) {
95 std::cerr <<
"NLogger: Failed to create log directory: " << e.what() << std::endl;
116 catch (
const std::exception & e) {
117 std::cerr <<
"NLogger: Failed to create log directory: " << e.what() << std::endl;
136 std::thread::id tid = std::this_thread::get_id();
145 std::ostringstream oss;
152 std::thread::id tid = std::this_thread::get_id();
161 std::ostringstream oss;
168 std::thread::id tid = std::this_thread::get_id();
174 return *(it->second);
181 std::string filename_base;
186 filename_base = thread_id;
190 for (
char & c : filename_base) {
191 if (c ==
'/' || c ==
'\\' || c ==
':' || c ==
'*' || c ==
'?' || c ==
'"' || c ==
'<' || c ==
'>' || c ==
'|') {
197 std::ostringstream filename;
200 auto stream = std::make_unique<std::ofstream>(filename.str(), std::ios::app);
202 if (!stream->is_open()) {
203 std::cerr <<
"NLogger: Failed to open log file: " << filename.str() << std::endl;
207 auto now = std::chrono::system_clock::now();
208 std::time_t now_time = std::chrono::system_clock::to_time_t(now);
209 *stream <<
"=== Log started at " << std::ctime(&now_time);
210 *stream <<
"=== Process: " <<
fgProcessName <<
" (PID: " << getpid() <<
")" << std::endl;
211 *stream <<
"=== Thread: " << thread_id << std::endl;
215 auto & ref = *stream;
229 case logs::Severity::kTrace:
return "TRACE";
230 case logs::Severity::kDebug:
return "DEBUG";
231 case logs::Severity::kInfo:
return "INFO";
232 case logs::Severity::kWarn:
return "WARN";
233 case logs::Severity::kError:
return "ERROR";
234 case logs::Severity::kFatal:
return "FATAL";
235 default:
return "UNKNOWN";
241 auto it = logs::fgSeverityMap.find(severity_str);
242 if (it != logs::fgSeverityMap.end()) {
246 return logs::Severity::kInfo;
249 void NLogger::Log(
const char * file,
int line, logs::Severity level,
const char * format, ...)
256 va_start(args, format);
259 auto now = std::chrono::system_clock::now();
260 std::time_t now_time = std::chrono::system_clock::to_time_t(now);
261 std::tm * now_tm = std::localtime(&now_time);
263 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
264 std::strftime(time_buf,
sizeof(time_buf),
"%Y-%m-%d %H:%M:%S", now_tm);
267 char message_buf[4096];
268 vsnprintf(message_buf,
sizeof(message_buf), format, args);
272 std::ostringstream log_line;
273 log_line <<
"[" << time_buf <<
"." << std::setfill(
'0') << std::setw(3) << ms.count() <<
"] "
276 if (level <= logs::Severity::kDebug4) {
277 log_line <<
"[" << std::filesystem::path(file).filename().string() <<
":" << line <<
"] ";
280 log_line << message_buf;
285 if (stream.is_open()) {
286 stream << log_line.str() << std::endl;
294 std::cout << log_line.str() << std::endl;
Thread-safe singleton logger with per-thread file output.
static std::string fgLogDirectory
Directory for log files.
std::mutex fStreamMapMutex
std::unordered_map< std::thread::id, std::string > fThreadNames
Map of thread IDs to custom thread names.
void CloseThreadStream(std::thread::id thread_id)
Closes and removes the output file stream associated with a specific thread.
static bool fgFileOutput
Flag for file output.
static void SetProcessName(const std::string &name)
Sets the name of the current process.
static std::mutex fgLoggerMutex
Mutex for thread-safe singleton access.
static std::string GetThreadName()
Retrieves the name of the current thread.
static logs::Severity fgMinSeverity
Minimum severity level for logging.
void Init()
Initializes the logger.
NLogger()
Constructs a new NLogger instance.
static void SetThreadName(const std::string &name, std::thread::id thread_id=std::this_thread::get_id())
Sets the name of a thread.
static bool fgConsoleOutput
Flag for console output.
static logs::Severity GetMinSeverity()
Gets the current minimum severity level for logging.
std::string GetThreadIdentifier()
Get thread name or ID as string.
std::ofstream & GetThreadStream()
Retrieves the thread-local output file stream for logging.
static void Log(const char *file, int line, logs::Severity level, const char *format,...)
Logs a formatted message with the specified severity level.
static std::string fgProcessName
Process name prefix for log files.
std::unordered_map< std::thread::id, std::unique_ptr< std::ofstream > > fThreadStreams
Map storing unique output file streams for each thread.
static logs::Severity GetSeverityFromString(const std::string &severity_str)
Parses a string to obtain the corresponding logs::Severity enum value.
virtual ~NLogger()
Destroys the NLogger instance.
static std::string SeverityToString(logs::Severity level)
Converts a logs::Severity enum value to its string representation.
static NLogger * Instance()
Returns the singleton instance of NLogger.
static void SetLogDirectory(const std::string &dir)
Sets the directory where log files will be stored.
void Cleanup()
Cleans up logger resources.