/****************************************************************************** * file lib/management/better_log.c * author YuLiang * version 1.0.0 * date 14-Sep-2021 * brief This file provides all the log operation functions. * ****************************************************************************** * Attention * *

© COPYRIGHT(c) 2021 LandPower

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of LandPower nor the names of its contributors may be used to * endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* 标准C库头文件. */ #include #include #include #include /* 用户代码头文件. */ #include /* Private define ------------------------------------------------------------*/ #define LOG_DB_LOCK pthread_mutex_lock(&log_sys->log_db_mutex) #define LOG_DB_UNLOCK pthread_mutex_unlock(&log_sys->log_db_mutex) #define LOG_OUT 0 #define LOG_SHOW 1 /* Private macro -------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ log_sys_t *log_sys; static char *log_str = NULL; /* log等级. */ static const log_lvl_t log_priority[] = { {LOG_LVL_ERR, "err"}, {LOG_LVL_WARN, "warn"}, {LOG_LVL_NOTIF, "notif"}, {LOG_LVL_INFO, "info"}, {LOG_LVL_DBG, "debug"}, {-1, NULL} }; /* log模块. */ static const log_module_t log_module_names[] = { {LOG_DEFAULT, "DEFAULT"}, {LOG_CLI, "CLI"}, {LOG_MEMORY, "MEMORY"}, {LOG_FIFO, "FIFO"}, {LOG_GPIO, "GPIO"}, {LOG_PD, "PD"}, {LOG_CAU, "CAU"}, {LOG_CSG, "CSG"}, {LOG_STORAGE, "STORAGE"}, {LOG_DEBUG, "DEBUG"}, {LOG_NETWORK, "NETWORK"}, {LOG_MQTT, "MQTT"}, {-1, NULL} }; /* Private function prototypes -----------------------------------------------*/ /* Internal functions --------------------------------------------------------*/ /* 文件log输出底层函数. */ static void _log_out_db(LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str) { log_sys_t *log = log_sys; char time_str[TIME_STR_LEN] = {0}; char *zErrMsg = NULL; static uint64_t logCnt = 0; time_string(log->timestamp_precision, time_str, sizeof(time_str)); LOG_DB_LOCK; snprintf(log_str, LOG_CMD_LEN, "INSERT INTO better_log (LEVEL,LOG) VALUES (%d, \"%s %s-%s: %s\"); ", priority, time_str, log_priority[priority].describe, log_module_names[module].describe, va_str ); //id 已设为自增 if (sqlite3_exec( log->db, log_str, 0, 0, &zErrMsg ) != SQLITE_OK) { printh("SQL INSERT error: %s\r\n", zErrMsg); sqlite3_free(zErrMsg); } /*定量清除log*/ logCnt++; if (logCnt >= 100) { clock_t begin,end; begin = clock(); log_clean(); end = clock(); printh("Log clean runtimer : %lf\r\n",(double)(end-begin)/CLOCKS_PER_SEC); logCnt = 0; } LOG_DB_UNLOCK; } /* 解日志输出fifo数据封装 */ static void _log_out(_log_out_t *data) { _log_out_db(data->module, data->lvl, data->log_out_str); } /* 串口日志输出 */ static void _log_out_std( LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str ) { log_sys_t *log = log_sys; char time_str[TIME_STR_LEN] = {0}; time_string(log->timestamp_precision, time_str, sizeof(time_str)); printh("%s %s-%s: %s\n", time_str, log_priority[priority].describe, log_module_names[module].describe, va_str); } /* 将日志按照格式串口打印 */ static int _log_show_print(void *data, int argc, char **argv, char **azColName) { printh("LOG id %s: %s\r\n", argv[0], argv[2]); return 0; } /* log打印底层函数 */ static void _log_show(_log_show_t *data) { log_sys_t *log = log_sys; char *zErrMsg = NULL; LOG_DB_LOCK; memset(log_str, 0, LOG_CMD_LEN); switch (data->type) { case LOG_SHOW_CNT: //打印指定数量的日志 snprintf(log_str, LOG_CMD_LEN, "SELECT * FROM better_log ORDER BY id DESC LIMIT %d;", data->param); break; case LOG_SHOW_LVL: //打印指定等级的日志 snprintf(log_str, LOG_CMD_LEN, "SELECT * FROM better_log WHERE LEVEL = %d ORDER BY id DESC LIMIT 100;", data->param); break; case LOG_SHOW_KEYWORD: //打印含关键词的日志 if (data->param <= 0) { snprintf(log_str, LOG_CMD_LEN, "SELECT * FROM better_log WHERE LOG GLOB \'*%s*\' ORDER BY id DESC;", data->log_show_str); } else { snprintf(log_str, LOG_CMD_LEN, "SELECT * FROM better_log WHERE LOG GLOB \'*%s*\' ORDER BY id DESC LIMIT %d;", data->log_show_str, data->param); } break; default: //打印所有日志 snprintf(log_str, LOG_CMD_LEN, "SELECT * FROM better_log ORDER BY id DESC;"); break; } if (sqlite3_exec(log->db, log_str, _log_show_print, 0, &zErrMsg) != SQLITE_OK) { printh("SQL show error: %s\r\n", zErrMsg); sqlite3_free(zErrMsg); } LOG_DB_UNLOCK; } /* 封装数据并写fifo */ int32_t _log_msg_send(uint32_t type, void *data) { _log_msg_t log_msg; /* 封装消息. */ log_msg.type = type; log_msg.data = data; /* 发送消息 */ if (fifo_write(log_sys->log_fifo_id, (void*)(&log_msg), sizeof(_log_msg_t)) != sizeof(_log_msg_t)) { DBG(DBG_M_FIFO, "LOG write ERROR!\r\n"); return E_ERROR; } return E_NONE; } /* 经由fifo输出日志 */ void _log_fifo_out(LOG_MODULE_E module, LOG_LVL_E priority, const char *va_str) { _log_out_t *log_data = NULL; log_data = XMALLOC_Q(MTYPE_LOG, sizeof(_log_out_t)); if (!log_data) { return; } log_data->module = module; log_data->lvl = priority; snprintf(log_data->log_out_str, LOG_STR_LEN, va_str); _log_msg_send(LOG_OUT, log_data); } /* 读取log fifo数据循环线程 */ void *_log_handle(void *arg) { _log_msg_t *recv_msg = NULL; while(1) { if (fifo_read(log_sys->log_fifo_id, (void **)&recv_msg) != 0) { DBG(DBG_M_FIFO, "ERROR at fifo %d read!\r\n", log_sys->log_fifo_id); continue; } if (recv_msg->type == LOG_OUT) //输出日志,写数据库 { _log_out((_log_out_t*) recv_msg->data); } else if (recv_msg->type == LOG_SHOW) //打印日志,读数据库 { _log_show((_log_show_t*) recv_msg->data); } XFREE(MTYPE_LOG, recv_msg->data); fifo_push(log_sys->log_fifo_id); } return NULL; } /* Interface functions -------------------------------------------------------*/ /* log打印输出函数. */ #define BLOG_FUNC(FUNCNAME,PRIORITY) \ void FUNCNAME(LOG_MODULE_E module, const char *format, ...) \ { \ char str[LOG_STR_LEN] = {0}; \ log_sys_t *log = log_sys; \ if (NULL == log) return; \ va_list args; \ va_start(args, format); \ vsnprintf(str, LOG_STR_LEN, format, args); \ va_end(args); \ if ((1 << PRIORITY) & log->enable_lvl[LOG_MODE_STDOUT]) \ _log_out_std(module, PRIORITY, str); \ if (((1 << PRIORITY) & log->enable_lvl[LOG_MODE_FILE]) && log->db){ \ if (log_sys->log_fifo_id) \ _log_fifo_out(module, PRIORITY, str); \ else \ _log_out_db(module, PRIORITY, str);} \ } BLOG_FUNC(log_err, LOG_LVL_ERR) BLOG_FUNC(log_warn, LOG_LVL_WARN) BLOG_FUNC(log_info, LOG_LVL_INFO) BLOG_FUNC(log_notice, LOG_LVL_NOTIF) BLOG_FUNC(log_debug, LOG_LVL_DBG) #undef BLOG_FUNC /* description: log输出. param: module -- 指定模块 priority -- 指定优先级 return: */ void log_out(LOG_MODULE_E module, LOG_LVL_E priority, const char *format, ...) { char str[LOG_STR_LEN] = {0}; \ log_sys_t *log = log_sys; if (NULL == log) return; va_list args; va_start(args, format); vsnprintf(str, LOG_STR_LEN, format, args); va_end (args); /* 串口log. */ if ((1 << priority) & log->enable_lvl[LOG_MODE_STDOUT]) _log_out_std(module, priority, str); /* 判断是否是文件输出 */ if (((1 << priority) & log->enable_lvl[LOG_MODE_FILE]) && log->db) _log_out_db(module, priority, str); } /* 打印日志函数 */ void log_show(int32_t show_cnt, LOG_LVL_E priority, const char *key_word) { _log_show_t *log_data = NULL; log_data = XMALLOC_Q(MTYPE_LOG, sizeof(_log_show_t)); if (!log_data) { return; } if (priority != LOG_LVL_MAX) // 按等级打印日志 { log_data->type = LOG_SHOW_LVL; log_data->param = priority; } else if (key_word != NULL) // 高级打印功能 { log_data->type = LOG_SHOW_KEYWORD; log_data->param = show_cnt; snprintf(log_data->log_show_str, LOG_STR_LEN, key_word); } else if (show_cnt > 0) // 按数量打印日志 { log_data->type = LOG_SHOW_CNT; log_data->param = show_cnt; } else { log_data->type = LOG_SHOW_MAX; } _log_msg_send(LOG_SHOW, log_data); } /* description: log系统初始化. param: return: (0)完成,(-1)失败 */ int32_t log_open() { log_sys_t *log = NULL; int32_t i = 0; int32_t rv = 0; char *sql = NULL; char *zErrMsg = NULL; /* 申请内存. */ log_str = XMALLOC(MTYPE_LOG, LOG_CMD_LEN); log_sys = XMALLOC(MTYPE_LOG, sizeof(log_sys_t)); if (NULL == log_sys) { return E_MEM; } log = log_sys; pthread_mutex_init(&log->log_db_mutex, NULL); /* 打开 log 数据库. */ log->filename = XSTRDUP(MTYPE_LOG, LOG_FILE); rv = sqlite3_open(log->filename, &log->db); if (rv) { log->db = NULL; printf("Can't open database: %s\r\n", sqlite3_errmsg(log->db)); return E_SYS_CALL; } /* 创建表. */ sql = "CREATE TABLE IF NOT EXISTS better_log(" \ "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," \ "LEVEL INT8 NOT NULL," \ "LOG TEXT);"; if (sqlite3_exec(log->db, sql, 0, 0, &zErrMsg) != SQLITE_OK) { printf("SQL create error: %s\r\n", zErrMsg); sqlite3_free(zErrMsg); } /* 设置默认log级别输出方式 */ for (i = 0; i < LOG_MODE_MAX; i++) { log->enable_lvl[i] = 0; } log->enable_lvl[LOG_MODE_FILE] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); log->enable_lvl[LOG_MODE_STDOUT] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); log->enable_lvl[LOG_MODE_MONITOR] = (1 << LOG_LVL_ERR) | (1 << LOG_LVL_WARN) | (1 << LOG_LVL_NOTIF); log->default_lvl = 0; return E_NONE; } /* description: 配置对应等级的log的输出方式. param: mode -- 输出方式 log_level -- 输出等级 return: */ void log_set_level(LOG_MODE_E mode, LOG_LVL_E log_level) { log_sys->enable_lvl[mode] |= (1 << log_level); } /* description: 取消对应等级的log的输出方式. param: mode -- 输出方式 log_level -- 输出等级 return: */ void log_unset_level(LOG_MODE_E mode, LOG_LVL_E log_level) { log_sys->enable_lvl[mode] &= ~(1 << log_level); } /* description: 根据传入的字符串返回相应的log优先级. param: lvl_name -- 输出等级的字符串 return: (LOG_LVL_E)输出等级 */ int32_t log_level_get_by_name(const char *lvl_name) { int32_t level = LOG_LVL_MAX; for (level = 0 ; log_priority[level].describe != NULL ; level++) if (!strncmp(lvl_name, log_priority[level].describe, 2)) return log_priority[level].lvl; return LOG_LVL_MAX; } /* 打印堆栈使用情况: */ /* description: 根据传入的字符串返回相应的log优先级. param: priority -- log优先级 return: */ void log_backtrace(int32_t priority) { void *array[BACKTRACE_SIZE] = {NULL}; int32_t size = 0; int32_t i = 0; char **strings = NULL; size = backtrace(array, BACKTRACE_SIZE); if ((size <= 0) || ((size_t)size > BACKTRACE_SIZE)) { log_err(LOG_DEFAULT, "Cannot get backtrace, returned invalid # of frames %d " "(valid range is between 1 and %d)", size, BACKTRACE_SIZE); return; } log_out(LOG_DEFAULT, priority, "Backtrace for %d stack frames:", size); strings = backtrace_symbols(array, size); if (!strings) { log_out(LOG_DEFAULT, priority, "Cannot get backtrace symbols (out of memory?)"); for (i = 0; i < size; i++) log_out(LOG_DEFAULT, priority, "[bt %d] %p", i, array[i]); } else { for (i = 0; i < size; i++) log_out(LOG_DEFAULT, priority, "[bt %d] %s",i,strings[i]); free(strings); } } /* 删除多余的 log 保留 5000 条. */ void log_clean(void) { log_sys_t *log = log_sys; char *zErrMsg = NULL; const char *sql = "delete from better_log where id<(select Max(id) from better_log)-5000;"; if (sqlite3_exec(log->db, sql, 0, 0, &zErrMsg) != SQLITE_OK) { printh("SQL delete error: %s\r\n", zErrMsg); sqlite3_free(zErrMsg); } } int32_t log_handle_init(void) { struct sched_param param; pthread_attr_t attr; pthread_t pid; int rv = 0; /* 初始化log fifo */ log_sys->log_fifo_id = fifo_create(LOG_DB_FIFO, 32); if (log_sys->log_fifo_id < 0) { log_out(LOG_DEFAULT, LOG_LVL_ERR, "Open fifo " LOG_DB_FIFO " error."); return E_ERROR; } /* 配置线程RR调度,优先级50 */ pthread_attr_init(&attr); param.sched_priority = 25; pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setschedparam(&attr, ¶m); pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); rv = pthread_create(&pid, &attr, _log_handle, NULL); if (rv != 0) { log_out(LOG_DEFAULT, LOG_LVL_ERR, "PD can't create log db pthread %d.", rv); return E_SYS_CALL; } else { thread_m_add("LOG_DB_THREAD", pid); } pthread_attr_destroy(&attr); return E_NONE; } /************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/