101 lines
2.8 KiB
C++
101 lines
2.8 KiB
C++
|
|
#include "native_logger.hpp"
|
||
|
|
|
||
|
|
#include <cerrno>
|
||
|
|
#include <cstring>
|
||
|
|
#include <mutex>
|
||
|
|
#include <string>
|
||
|
|
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <sys/types.h>
|
||
|
|
#include <time.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
|
||
|
|
namespace netcore {
|
||
|
|
|
||
|
|
static std::mutex g_mu;
|
||
|
|
static int g_fd = -1;
|
||
|
|
static std::string g_path = "netcore.log";
|
||
|
|
static LogLevel g_level = LogLevel::kDebug; //LogLevel::kInfo;
|
||
|
|
|
||
|
|
static const char* level_name(LogLevel lvl) {
|
||
|
|
switch (lvl) {
|
||
|
|
case LogLevel::kError: return "E";
|
||
|
|
case LogLevel::kWarn: return "W";
|
||
|
|
case LogLevel::kInfo: return "I";
|
||
|
|
case LogLevel::kDebug: return "D";
|
||
|
|
default: return "?";
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void ensure_open_locked() {
|
||
|
|
if (g_path.empty()) return;
|
||
|
|
if (g_fd >= 0) return;
|
||
|
|
g_fd = ::open(g_path.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0644);
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_log_file(const std::string& path) {
|
||
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
||
|
|
g_path = path;
|
||
|
|
if (g_fd >= 0) {
|
||
|
|
::close(g_fd);
|
||
|
|
g_fd = -1;
|
||
|
|
}
|
||
|
|
ensure_open_locked();
|
||
|
|
}
|
||
|
|
|
||
|
|
void set_log_level(LogLevel level) {
|
||
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
||
|
|
g_level = level;
|
||
|
|
}
|
||
|
|
|
||
|
|
void log(LogLevel level, const std::string& msg) {
|
||
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
||
|
|
if (static_cast<int>(level) > static_cast<int>(g_level)) return;
|
||
|
|
if (g_path.empty()) return;
|
||
|
|
|
||
|
|
ensure_open_locked();
|
||
|
|
if (g_fd < 0) {
|
||
|
|
// Last resort: stderr (avoid any Python APIs)
|
||
|
|
::write(STDERR_FILENO, msg.c_str(), msg.size());
|
||
|
|
::write(STDERR_FILENO, "\n", 1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Timestamp: epoch milliseconds (simple and cheap)
|
||
|
|
struct timespec ts;
|
||
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||
|
|
// long long ms = (long long)ts.tv_sec * 1000LL + ts.tv_nsec / 1000000LL;
|
||
|
|
// 1. 将秒数转换为本地时间结构体 struct tm
|
||
|
|
struct tm *tm_info = localtime(&ts.tv_sec);
|
||
|
|
|
||
|
|
// 2. 准备一个缓冲区来存储时间字符串
|
||
|
|
char buffer[30];
|
||
|
|
|
||
|
|
// 3. 格式化秒的部分
|
||
|
|
// 格式: 年-月-日 时:分:秒
|
||
|
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
|
||
|
|
|
||
|
|
// 4. 计算毫秒部分并追加到字符串中
|
||
|
|
// ts.tv_nsec 是纳秒,除以 1,000,000 得到毫秒
|
||
|
|
char ms_buffer[8];
|
||
|
|
snprintf(ms_buffer, sizeof(ms_buffer), ".%03ld", ts.tv_nsec / 1000000);
|
||
|
|
|
||
|
|
// Build one line to keep writes atomic-ish
|
||
|
|
char head[256];
|
||
|
|
int n = ::snprintf(head, sizeof(head), "[%s%s] [%s] ", buffer, ms_buffer, level_name(level));
|
||
|
|
if (n < 0) n = 0;
|
||
|
|
|
||
|
|
::write(g_fd, head, (size_t)n);
|
||
|
|
::write(g_fd, msg.c_str(), msg.size());
|
||
|
|
::write(g_fd, "\n", 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
void log_debug(const std::string& msg) { log(LogLevel::kDebug, msg); }
|
||
|
|
void log_info (const std::string& msg) { log(LogLevel::kInfo, msg); }
|
||
|
|
void log_warn (const std::string& msg) { log(LogLevel::kWarn, msg); }
|
||
|
|
void log_error(const std::string& msg) { log(LogLevel::kError, msg); }
|
||
|
|
|
||
|
|
} // namespace netcore
|
||
|
|
|