| | |
| | | #include <string.h> |
| | | #include <time.h> |
| | | #include <sys/time.h> |
| | | #include <chrono> |
| | | #include <stdarg.h> |
| | | #include "log.h" |
| | | #include <pthread.h> |
| | | #include <fstream> |
| | | #include <iostream> |
| | | |
| | | using namespace std; |
| | | |
| | | Log::Log() |
| | | { |
| | | m_count = 0; |
| | | m_is_async = false; |
| | | } |
| | | // æé 彿° |
| | | Log::Log() : m_count(0), m_is_async(false) {} |
| | | |
| | | Log::~Log() |
| | | { |
| | | if (m_fp != NULL) |
| | | { |
| | | fclose(m_fp); |
| | | // ææå½æ° |
| | | Log::~Log() { |
| | | if (m_fp.is_open()) { |
| | | m_fp.close(); // å
³éæ¥å¿æä»¶ |
| | | } |
| | | } |
| | | |
| | | bool Log::init(const char *file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size) |
| | | { |
| | | if (max_queue_size >= 1) |
| | | { |
| | | m_is_async = true; |
| | | m_log_queue = new block_queue<string>(max_queue_size); |
| | | pthread_t tid; |
| | | pthread_create(&tid, NULL, flush_log_thread, NULL); |
| | | } |
| | | |
| | | m_close_log = close_log; |
| | | m_log_buf_size = log_buf_size; |
| | | m_buf = new char[m_log_buf_size]; |
| | | memset(m_buf, '\0', m_log_buf_size); |
| | | m_split_lines = split_lines; |
| | | |
| | | time_t t = time(NULL); |
| | | struct tm *sys_tm = localtime(&t); |
| | | const char *p = strrchr(file_name, '/'); |
| | | char log_full_name[256] = {0}; |
| | | |
| | | if (p == NULL) |
| | | { |
| | | snprintf(log_full_name, 255, "%d_%02d_%02d_%s", sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, file_name); |
| | | } |
| | | else |
| | | { |
| | | strcpy(log_name, p + 1); |
| | | strncpy(dir_name, file_name, p - file_name + 1); |
| | | snprintf(log_full_name, 255, "%s%d_%02d_%02d_%s", dir_name, sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, log_name); |
| | | } |
| | | |
| | | m_today = sys_tm->tm_mday; |
| | | m_fp = fopen(log_full_name, "a"); |
| | | if (m_fp == NULL) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | void Log::write_log(int level, const char *format, ...) |
| | | { |
| | | struct timeval now; |
| | | gettimeofday(&now, NULL); |
| | | time_t t = now.tv_sec; |
| | | struct tm *sys_tm = localtime(&t); |
| | | char s[16] = {0}; |
| | | |
| | | switch (level) |
| | | { |
| | | case 0: strcpy(s, "[debug]:"); break; |
| | | case 1: strcpy(s, "[info]:"); break; |
| | | case 2: strcpy(s, "[warn]:"); break; |
| | | case 3: strcpy(s, "[erro]:"); break; |
| | | default: strcpy(s, "[info]:"); break; |
| | | } |
| | | |
| | | m_mutex.lock(); |
| | | m_count++; |
| | | |
| | | if (m_today != sys_tm->tm_mday || m_count % m_split_lines == 0) |
| | | { |
| | | char new_log[256] = {0}; |
| | | fflush(m_fp); |
| | | fclose(m_fp); |
| | | char tail[16] = {0}; |
| | | snprintf(tail, 16, "%d_%02d_%02d_", sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday); |
| | | |
| | | if (m_today != sys_tm->tm_mday) |
| | | { |
| | | snprintf(new_log, 255, "%s%s%s", dir_name, tail, log_name); |
| | | m_today = sys_tm->tm_mday; |
| | | m_count = 0; |
| | | //弿¥åå
¥ |
| | | void Log::async_write_log() { |
| | | while (m_is_async) { |
| | | char* log; |
| | | if (m_log_queue->pop(log)) { |
| | | ParsedLog parsed_log = parseLog(log); |
| | | write_log(INFO, "%s", log); // å
åå
¥æ¥å¿æä»¶ |
| | | writeLogToDatabase(parsed_log); // ååå
¥æ°æ®åº |
| | | delete[] log; // éæ¾å
å |
| | | } |
| | | else |
| | | { |
| | | snprintf(new_log, 255, "%s%s%s.%lld", dir_name, tail, log_name, m_count / m_split_lines); |
| | | } |
| | | m_fp = fopen(new_log, "a"); |
| | | } |
| | | |
| | | // å¨åå
¥ä¹åæ£æ¥æ¥å¿æä»¶å¤§å° |
| | | if (check_log_size()) |
| | | { |
| | | rotate_logs(); // 妿è¶
è¿æå¤§æä»¶å¤§å°ï¼è¿è¡æ¥å¿è½®æ¢ |
| | | } |
| | | |
| | | va_list valst; |
| | | va_start(valst, format); |
| | | |
| | | string log_str; |
| | | int n = snprintf(m_buf, 48, "%d-%02d-%02d %02d:%02d:%02d.%06ld %s ", |
| | | sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, |
| | | sys_tm->tm_hour, sys_tm->tm_min, sys_tm->tm_sec, now.tv_usec, s); |
| | | int m = vsnprintf(m_buf + n, m_log_buf_size - n - 1, format, valst); |
| | | m_buf[n + m] = '\n'; |
| | | m_buf[n + m + 1] = '\0'; |
| | | log_str = m_buf; |
| | | |
| | | m_mutex.unlock(); |
| | | |
| | | if (m_is_async && !m_log_queue->full()) |
| | | { |
| | | m_log_queue->push(log_str); |
| | | } |
| | | else |
| | | { |
| | | m_mutex.lock(); |
| | | fputs(log_str.c_str(), m_fp); |
| | | m_mutex.unlock(); |
| | | } |
| | | |
| | | va_end(valst); |
| | | } |
| | | |
| | | void Log::flush(void) |
| | | { |
| | | m_mutex.lock(); |
| | | fflush(m_fp); |
| | | m_mutex.unlock(); |
| | | } |
| | | |
| | | // æ·»å çæä»¶å¤§å°æ£æ¥å½æ° |
| | | bool Log::check_log_size() |
| | | { |
| | | if (m_fp) |
| | | { |
| | | fseek(m_fp, 0, SEEK_END); |
| | | long file_size = ftell(m_fp); |
| | | return file_size >= max_size; // è¿åæä»¶æ¯å¦è¶
åºéå¶ |
| | | bool Log::check_log_size() { |
| | | if (m_fp.tellp() >= max_size) { |
| | | rotate_logs(); // 妿æ¥å¿æä»¶å¤§å°è¶
è¿æå¤§å¼ï¼è½®æ¢æ¥å¿ |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | void Log::rotate_logs() |
| | | { |
| | | fclose(m_fp); // å
³éå½åæ¥å¿æä»¶ |
| | | char new_log[256] = {0}; |
| | | snprintf(new_log, sizeof(new_log), "%s/%s.%lld", dir_name, log_name, m_count / m_split_lines); |
| | | m_fp = fopen(new_log, "a"); // æå¼æ°çæ¥å¿æä»¶ |
| | | |
| | | // æ¥æ¶åå§æ¥å¿å¹¶è§£æ |
| | | void Log::receiveLog(const char* raw_log) { |
| | | ParsedLog parsed_log = parseLog(raw_log); |
| | | write_log(levelToInt(parsed_log.level), "%s [%s] %s", |
| | | parsed_log.timestamp, |
| | | parsed_log.device_id, |
| | | parsed_log.content); |
| | | } |
| | | |
| | | //åå
¥æ°æ®åº |
| | | void Log::writeLogToDatabase(const ParsedLog& log) { |
| | | const char* sql = "INSERT INTO parsed_logs (timestamp, device_id, level, content, source, user_id) VALUES (?, ?, ?, ?, ?, ?)"; |
| | | sqlite3_stmt* stmt; |
| | | |
| | | if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| | | cerr << "Error: Failed to prepare statement: " << sqlite3_errmsg(db) << endl; |
| | | return; |
| | | } |
| | | |
| | | sqlite3_bind_text(stmt, 1, log.timestamp, -1, SQLITE_STATIC); |
| | | sqlite3_bind_text(stmt, 2, log.device_id, -1, SQLITE_STATIC); |
| | | sqlite3_bind_text(stmt, 3, log.level, -1, SQLITE_STATIC); |
| | | sqlite3_bind_text(stmt, 4, log.content, -1, SQLITE_STATIC); |
| | | sqlite3_bind_text(stmt, 5, log.source, -1, SQLITE_STATIC); |
| | | sqlite3_bind_text(stmt, 6, log.user_id, -1, SQLITE_STATIC); |
| | | |
| | | if (sqlite3_step(stmt) != SQLITE_DONE) { |
| | | cerr << "Error: Failed to execute statement: " << sqlite3_errmsg(db) << endl; |
| | | } |
| | | |
| | | sqlite3_finalize(stmt); // æ¸
çåå¤è¯å¥ |
| | | } |
| | | |
| | | |
| | | // è§£ææ¥å¿å符串 |
| | | ParsedLog Log::parseLog(const char* log) { |
| | | ParsedLog parsed_log; |
| | | char level[16]; |
| | | |
| | | // è¯»åæ¶é´æ³ã设å¤ID忥å¿çº§å« |
| | | sscanf(log, "%s %s %s %[^\n]", parsed_log.timestamp, parsed_log.device_id, level, parsed_log.content); |
| | | // 设置解æåççº§å« |
| | | for (int i = 0; i < 16; ++i) { |
| | | parsed_log.level[i] = level[i]; |
| | | } |
| | | |
| | | return parsed_log; |
| | | } |
| | | |
| | | // åå§åæ¥å¿ç³»ç» |
| | | bool Log::init(const char* file_name, int close_log, int log_buf_size, int split_lines, int max_queue_size) { |
| | | if (max_queue_size >= 1) { |
| | | m_is_async = true; // å¯ç¨å¼æ¥åå
¥ |
| | | m_log_queue = new block_queue<char*>(max_queue_size); // å建é»å¡éå |
| | | std::thread(&Log::flush_log_thread, this).detach(); // åå»ºå·æ°æ¥å¿çº¿ç¨ |
| | | } |
| | | |
| | | m_close_log = close_log; // 设置å
³éæ¥å¿æ å¿ |
| | | m_log_buf_size = log_buf_size; // 设置ç¼å²åºå¤§å° |
| | | m_buf = new char[m_log_buf_size]; // åé
ç¼å²åº |
| | | memset(m_buf, '\0', m_log_buf_size); // æ¸
空ç¼å²åº |
| | | m_split_lines = split_lines; // 设置æå¤§è¡æ° |
| | | |
| | | time_t t = time(NULL); |
| | | struct tm *sys_tm = localtime(&t); |
| | | char log_full_name[256]; |
| | | |
| | | // æ ¹æ®æä»¶è·¯å¾åå½åæ¥æçæå®æ´æ¥å¿æä»¶å |
| | | if (strchr(file_name, '/') == nullptr) { |
| | | sprintf(log_full_name, "%d_%d_%d_%s", |
| | | sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, file_name); |
| | | } else { |
| | | char log_name[128]; |
| | | char dir_name[128]; |
| | | char* last_slash = strrchr(file_name, '/'); |
| | | int dir_len = last_slash - file_name + 1; |
| | | strncpy(dir_name, file_name, dir_len); |
| | | dir_name[dir_len] = '\0'; |
| | | strcpy(log_name, last_slash + 1); |
| | | |
| | | sprintf(log_full_name, "%s%d_%d_%d_%s", |
| | | dir_name, sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday, log_name); |
| | | } |
| | | |
| | | m_today = sys_tm->tm_mday; // è®°å½ä»å¤©çæ¥æ |
| | | m_fp.open(log_full_name, std::ios::out | std::ios::app); // æå¼æ¥å¿æä»¶ |
| | | if (!m_fp.is_open()) { |
| | | cerr << "Error: Could not open log file: " << log_full_name << endl; |
| | | return false; // æå¼å¤±è´¥ |
| | | } |
| | | |
| | | return true; // åå§åæå |
| | | } |
| | | |
| | | //æ¥è¯¢æ¥å¿èæ |
| | | void Log::queryLogs(const char *device_id) { |
| | | const char* sql = "SELECT * FROM parsed_logs WHERE device_id = ?"; |
| | | sqlite3_stmt* stmt; |
| | | |
| | | if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| | | cerr << "Error: Failed to prepare statement: " << sqlite3_errmsg(db) << endl; |
| | | return; |
| | | } |
| | | |
| | | sqlite3_bind_text(stmt, 1, device_id, -1, SQLITE_STATIC); |
| | | |
| | | while (sqlite3_step(stmt) == SQLITE_ROW) { |
| | | cout << "Timestamp: " << sqlite3_column_text(stmt, 1) << ", " |
| | | << "Device ID: " << sqlite3_column_text(stmt, 2) << ", " |
| | | << "Level: " << sqlite3_column_text(stmt, 3) << ", " |
| | | << "Content: " << sqlite3_column_text(stmt, 4) << ", " |
| | | << "Source: " << sqlite3_column_text(stmt, 5) << ", " |
| | | << "User ID: " << sqlite3_column_text(stmt, 6) << endl; |
| | | } |
| | | |
| | | sqlite3_finalize(stmt); // æ¸
çåå¤è¯å¥ |
| | | } |
| | | |
| | | |
| | | |
| | | // åå
¥æ¥å¿ |
| | | void Log::write_log(int level, const char *format, ...) { |
| | | auto now = std::chrono::system_clock::now(); |
| | | auto now_time_t = std::chrono::system_clock::to_time_t(now); |
| | | struct tm *sys_tm = localtime(&now_time_t); |
| | | |
| | | char s[32]; |
| | | |
| | | // æ ¹æ®æ¥å¿çº§å«è®¾ç½®åç¼ |
| | | switch (level) { |
| | | case DEBUG: strcpy(s, "[debug]: "); break; |
| | | case INFO: strcpy(s, "[info]: "); break; |
| | | case WARN: strcpy(s, "[warn]: "); break; |
| | | case ERROR: strcpy(s, "[error]: "); break; |
| | | default: strcpy(s, "[info]: "); break; |
| | | } |
| | | |
| | | m_mutex.lock(); // éå®äºæ¥ä½ |
| | | m_count++; // å¢å æ¥å¿è®¡æ° |
| | | |
| | | // æ£æ¥æ¯å¦éè¦è½®æ¢æ¥å¿ |
| | | if (m_today != sys_tm->tm_mday || m_count % m_split_lines == 0) { |
| | | rotate_logs(); // è°ç¨æ¥å¿è½®æ¢å½æ° |
| | | } |
| | | |
| | | // åå
¥æ¥å¿å
容 |
| | | va_list valist; |
| | | va_start(valist, format); |
| | | vsnprintf(m_buf, m_log_buf_size, format, valist); // åå
¥æ¥å¿å
容 |
| | | |
| | | m_fp << s << m_buf << std::endl; // 使ç¨ofstreamåå
¥æä»¶ |
| | | va_end(valist); |
| | | |
| | | m_mutex.unlock(); // è§£éäºæ¥ä½ |
| | | |
| | | // 弿¥åå
¥æ¥å¿ |
| | | if (m_is_async && !m_log_queue->full()) { |
| | | m_log_queue->push(m_buf); // å°æ¥å¿æ¨å
¥éå |
| | | } |
| | | } |
| | | |
| | | // å·æ°æ¥å¿æä»¶ |
| | | void Log::flush(void) { |
| | | m_mutex.lock(); // éå®äºæ¥ä½ |
| | | m_fp.flush(); // å·æ°æä»¶ |
| | | m_mutex.unlock(); // è§£éäºæ¥ä½ |
| | | } |
| | | |
| | | // å·æ°æ¥å¿çº¿ç¨ |
| | | void Log::flush_log_thread() { |
| | | while (m_is_async) { |
| | | // ä»éåä¸è·åæ¥å¿å¹¶åå
¥ |
| | | char* log; |
| | | if (m_log_queue->pop(log)) { |
| | | write_log(INFO, "%s", log); |
| | | } |
| | | } |
| | | } |
| | | |
| | | //转æ¢å符串 |
| | | const char* Log::log_level_to_string(int level) { |
| | | switch (level) { |
| | | case DEBUG: return "[debug]: "; |
| | | case INFO: return "[info]: "; |
| | | case WARN: return "[warn]: "; |
| | | case ERROR: return "[error]: "; |
| | | default: return "[info]: "; // é»è®¤è¿åINFO |
| | | } |
| | | } |
| | | |
| | | // æ¥å¿çº§å«è½¬æ¢ä¸ºæ´æ° |
| | | int Log::levelToInt(const char* level) { |
| | | if (strcmp(level, "[debug]:") == 0) return DEBUG; |
| | | if (strcmp(level, "[info]:") == 0) return INFO; |
| | | if (strcmp(level, "[warn]:") == 0) return WARN; |
| | | if (strcmp(level, "[error]:") == 0) return ERROR; |
| | | return INFO; // é»è®¤è¿åINFO |
| | | } |
| | | |
| | | //æå¼å
³éæ°æ®åº |
| | | void Log::openDatabase(const char *db_name) { |
| | | if (sqlite3_open(db_name, &db) != SQLITE_OK) { |
| | | cerr << "æä¸å¼æ°æ®åºï¼ï¼éè¯¯ï¼ " << sqlite3_errmsg(db) << endl; |
| | | } |
| | | } |
| | | |
| | | void Log::closeDatabase() { |
| | | if (db) { |
| | | sqlite3_close(db); |
| | | } |
| | | } |
| | | |
| | | // æ¥å¿è½®æ¢å½æ° |
| | | void Log::rotate_logs() { |
| | | // å
³éå½åæä»¶ |
| | | m_fp.close(); |
| | | |
| | | // çææ°æä»¶å |
| | | time_t t = time(NULL); |
| | | struct tm *sys_tm = localtime(&t); |
| | | char new_log_name[256]; |
| | | sprintf(new_log_name, "%d_%d_%d_log.txt", |
| | | sys_tm->tm_year + 1900, sys_tm->tm_mon + 1, sys_tm->tm_mday); |
| | | |
| | | // æå¼æ°çæ¥å¿æä»¶ |
| | | m_fp.open(new_log_name, std::ios::out | std::ios::app); |
| | | if (!m_fp.is_open()) { |
| | | cerr << "Error: Could not open new log file: " << new_log_name << endl; |
| | | return; |
| | | } |
| | | |
| | | m_today = sys_tm->tm_mday; // æ´æ°ä»å¤©çæ¥æ |
| | | m_count = 0; // éç½®æ¥å¿è®¡æ° |
| | | } |