/******************************************************************************
* 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 ****************/