| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- #include "logger.h"
- #include <iostream>
- #include <ctime>
- #include <fstream>
- Logger& Logger::getInstance() {
- static Logger instance;
- return instance;
- }
- Logger::~Logger() {
- close();
- }
- void Logger::initialize(bool enableFileLogging, const std::string& logFilePath, LogLevel minLevel) {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_fileLoggingEnabled = enableFileLogging;
- m_logFilePath = logFilePath;
- m_internalMinLevel = minLevel;
- m_httpMinLevel = minLevel;
- // Perform systemd detection once during initialization
- if (!m_systemdDetected) {
- m_isRunningUnderSystemd = detectSystemd();
- m_systemdDetected = true;
- }
- if (m_fileLoggingEnabled && !m_logFilePath.empty()) {
- m_logFile.open(m_logFilePath, std::ios::app);
- if (!m_logFile.is_open()) {
- std::cerr << "<3>ERROR: Failed to open log file: " << m_logFilePath << std::endl;
- m_fileLoggingEnabled = false;
- } else {
- // Write startup marker
- m_logFile << "\n=== Log started at " << getCurrentTimestamp() << " ===" << std::endl;
- m_logFile.flush();
- }
- }
- }
- void Logger::setLogLevel(LoggerType type, LogLevel level) {
- std::lock_guard<std::mutex> lock(m_mutex);
- if (type == LoggerType::HTTP) {
- m_httpMinLevel = level;
- } else {
- m_internalMinLevel = level;
- }
- }
- LogLevel Logger::getLogLevel(LoggerType type) const {
- std::lock_guard<std::mutex> lock(m_mutex);
- return (type == LoggerType::HTTP) ? m_httpMinLevel : m_internalMinLevel;
- }
- void Logger::close() {
- std::lock_guard<std::mutex> lock(m_mutex);
- if (m_logFile.is_open()) {
- m_logFile << "=== Log closed at " << getCurrentTimestamp() << " ===" << std::endl;
- m_logFile.close();
- }
- }
- std::string Logger::getCurrentTimestamp() {
- auto now = std::chrono::system_clock::now();
- auto time_t_now = std::chrono::system_clock::to_time_t(now);
- auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
- now.time_since_epoch()) % 1000;
- std::tm tm_buf;
- localtime_r(&time_t_now, &tm_buf);
- std::ostringstream oss;
- oss << std::put_time(&tm_buf, "%Y-%m-%d %H:%M:%S");
- oss << '.' << std::setfill('0') << std::setw(3) << ms.count();
- return oss.str();
- }
- std::string Logger::levelToString(LogLevel level) {
- switch (level) {
- case LogLevel::DEBUG: return "DEBUG";
- case LogLevel::INFO: return "INFO";
- case LogLevel::WARNING: return "WARNING";
- case LogLevel::ERROR: return "ERROR";
- default: return "UNKNOWN";
- }
- }
- std::string Logger::typeToString(LoggerType type) {
- switch (type) {
- case LoggerType::HTTP: return "HTTP";
- case LoggerType::INTERNAL: return "INTERNAL";
- default: return "UNKNOWN";
- }
- }
- std::string Logger::levelToSystemdPriority(LogLevel level) {
- // systemd journal priority levels:
- // 0=emerg, 1=alert, 2=crit, 3=err, 4=warning, 5=notice, 6=info, 7=debug
- switch (level) {
- case LogLevel::DEBUG: return "<7>"; // debug
- case LogLevel::INFO: return "<6>"; // info
- case LogLevel::WARNING: return "<4>"; // warning
- case LogLevel::ERROR: return "<3>"; // error
- default: return "<6>";
- }
- }
- void Logger::log(LogLevel level, const std::string& message, LoggerType type) {
- LogLevel minLevel = (type == LoggerType::HTTP) ? m_httpMinLevel : m_internalMinLevel;
-
- if (level < minLevel) {
- return;
- }
- std::lock_guard<std::mutex> lock(m_mutex);
- std::string timestamp = getCurrentTimestamp();
- std::string levelStr = levelToString(level);
- std::string typeStr = typeToString(type);
- std::string priority = levelToSystemdPriority(level);
- // Console output with systemd priority prefix and type
- std::cout << priority << "[" << timestamp << "] "
- << typeStr << " " << levelStr << ": " << message << std::endl;
- // File output (without systemd priority, with type)
- if (m_fileLoggingEnabled && m_logFile.is_open()) {
- m_logFile << "[" << timestamp << "] "
- << typeStr << " " << levelStr << ": " << message << std::endl;
- m_logFile.flush();
- }
- }
- bool Logger::isRunningUnderSystemd() {
- std::lock_guard<std::mutex> lock(m_mutex);
-
- // Perform detection if not already done
- if (!m_systemdDetected) {
- m_isRunningUnderSystemd = detectSystemd();
- m_systemdDetected = true;
- }
-
- return m_isRunningUnderSystemd;
- }
- bool Logger::detectSystemd() {
- // Method 1: Try sd_booted() if systemd development libraries are available
- // This is the most reliable method when available
- #ifdef HAVE_SYSTEMD
- // If we have systemd development libraries, we could use sd_booted()
- // But we'll implement fallback methods for broader compatibility
- #endif
-
- // Method 2: Check for NOTIFY_SOCKET environment variable
- // This is set by systemd when using sd_notify()
- const char* notifySocket = std::getenv("NOTIFY_SOCKET");
- if (notifySocket && notifySocket[0] != '\0') {
- return true;
- }
-
- // Method 3: Check for JOURNAL_STREAM environment variable
- // This is set by systemd for services with StandardOutput=journal
- const char* journalStream = std::getenv("JOURNAL_STREAM");
- if (journalStream && journalStream[0] != '\0') {
- return true;
- }
-
- // Method 4: Check cgroup filesystem for systemd
- // Look for systemd-specific cgroup paths
- std::ifstream cgroupFile("/proc/1/cgroup");
- if (cgroupFile.is_open()) {
- std::string line;
- while (std::getline(cgroupFile, line)) {
- if (line.find("systemd") != std::string::npos) {
- return true;
- }
- }
- cgroupFile.close();
- }
-
- // Method 5: Check if /sys/fs/cgroup/systemd exists
- struct stat sb;
- if (stat("/sys/fs/cgroup/systemd", &sb) == 0 && S_ISDIR(sb.st_mode)) {
- return true;
- }
-
- // Method 6: Check PID 1 command line for "systemd"
- std::ifstream cmdlineFile("/proc/1/cmdline");
- if (cmdlineFile.is_open()) {
- std::string cmdline;
- std::getline(cmdlineFile, cmdline);
- cmdlineFile.close();
-
- // cmdline contains null-separated arguments, replace nulls with spaces for searching
- for (size_t i = 0; i < cmdline.length(); ++i) {
- if (cmdline[i] == '\0') {
- cmdline[i] = ' ';
- }
- }
-
- if (cmdline.find("systemd") != std::string::npos) {
- return true;
- }
- }
-
- return false;
- }
|