46 if (
const char * env_severity = getenv(
"NDMSPC_LOG_LEVEL")) {
49 std::cout <<
"NLogger: Setting log level to '" << env_severity <<
"' ... " << std::endl;
52 if (
const char * env_logdir = getenv(
"NDMSPC_LOG_DIR")) {
57 if (
const char * env_process_name = getenv(
"NDMSPC_PROCESS_NAME")) {
66 if (
const char * env_file_output = getenv(
"NDMSPC_LOG_FILE")) {
67 std::string value(env_file_output);
68 fgFileOutput = (value ==
"1" || value ==
"true" || value ==
"TRUE");
71 if (
const char * env_console_output = getenv(
"NDMSPC_LOG_CONSOLE")) {
72 std::string value(env_console_output);
73 if (value ==
"0" || value ==
"false" || value ==
"FALSE") {
75 gErrorIgnoreLevel = kFatal;
77 else if (value ==
"1" || value ==
"true" || value ==
"TRUE") {
83 std::thread::id main_tid = std::this_thread::get_id();
84 std::string main_thread_name =
"main";
87 if (
const char * env_main_thread = getenv(
"NDMSPC_MAIN_THREAD_NAME")) {
88 main_thread_name = env_main_thread;
98 catch (
const std::exception & e) {
99 std::cerr <<
"NLogger: Failed to create log directory: " << e.what() << std::endl;
120 catch (
const std::exception & e) {
121 std::cerr <<
"NLogger: Failed to create log directory: " << e.what() << std::endl;
140 std::thread::id tid = std::this_thread::get_id();
149 std::ostringstream oss;
156 std::thread::id tid = std::this_thread::get_id();
165 std::ostringstream oss;
172 std::thread::id tid = std::this_thread::get_id();
178 return *(it->second);
185 std::string filename_base;
190 filename_base = thread_id;
194 for (
char & c : filename_base) {
195 if (c ==
'/' || c ==
'\\' || c ==
':' || c ==
'*' || c ==
'?' || c ==
'"' || c ==
'<' || c ==
'>' || c ==
'|') {
201 std::ostringstream filename;
204 auto stream = std::make_unique<std::ofstream>(filename.str(), std::ios::app);
206 if (!stream->is_open()) {
207 std::cerr <<
"NLogger: Failed to open log file: " << filename.str() << std::endl;
211 auto now = std::chrono::system_clock::now();
212 std::time_t now_time = std::chrono::system_clock::to_time_t(now);
213 *stream <<
"=== Log started at " << std::ctime(&now_time);
214 *stream <<
"=== Process: " <<
fgProcessName <<
" (PID: " << getpid() <<
")" << std::endl;
215 *stream <<
"=== Thread: " << thread_id << std::endl;
219 auto & ref = *stream;
233 case logs::Severity::kTrace:
return "TRACE";
234 case logs::Severity::kDebug:
return "DEBUG";
235 case logs::Severity::kInfo:
return "INFO";
236 case logs::Severity::kWarn:
return "WARN";
237 case logs::Severity::kError:
return "ERROR";
238 case logs::Severity::kFatal:
return "FATAL";
239 default:
return "UNKNOWN";
245 auto it = logs::fgSeverityMap.find(severity_str);
246 if (it != logs::fgSeverityMap.end()) {
250 return logs::Severity::kInfo;
253 void NLogger::Log(
const char * file,
int line, logs::Severity level,
const char * format, ...)
260 va_start(args, format);
263 auto now = std::chrono::system_clock::now();
264 std::time_t now_time = std::chrono::system_clock::to_time_t(now);
265 std::tm * now_tm = std::localtime(&now_time);
267 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
268 std::strftime(time_buf,
sizeof(time_buf),
"%Y-%m-%d %H:%M:%S", now_tm);
271 char message_buf[4096];
272 vsnprintf(message_buf,
sizeof(message_buf), format, args);
276 std::ostringstream log_line;
277 log_line <<
"[" << time_buf <<
"." << std::setfill(
'0') << std::setw(3) << ms.count() <<
"] "
280 if (level <= logs::Severity::kDebug4) {
281 log_line <<
"[" << std::filesystem::path(file).filename().string() <<
":" << line <<
"] ";
284 log_line << message_buf;
289 if (stream.is_open()) {
290 stream << log_line.str() << std::endl;
298 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.