You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

556 lines
17 KiB
C

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/******************************************************************************
* 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
*
* <h2><center>&copy; COPYRIGHT(c) 2021 LandPower</center></h2>
*
* 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 <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <execinfo.h>
/* 用户代码头文件. */
#include <fifo.h>
/* Private define ------------------------------------------------------------*/
#define LOG_LOCK pthread_mutex_lock( &log_sys->log_mutex )
#define LOG_UNLOCK pthread_mutex_unlock( &log_sys->log_mutex )
#define LOG_OUT_LOCK pthread_mutex_lock( &log_sys->log_out_mutex )
#define LOG_OUT_UNLOCK pthread_mutex_unlock( &log_sys->log_out_mutex )
#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_fifo_t log_fifo;
log_sys_t *log_sys;
_log_out_t *log_out_struct;
_log_show_t *log_show_struct;
static char *log_str = NULL;
static char *log_out_va_str = NULL;
static char *log_fifo_va_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_DAU, "DAU"},
{LOG_CSG, "CSG"},
{LOG_STORAGE, "STORAGE"},
{LOG_DEBUG, "DEBUG"},
{-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;
memset( log_str, 0, LOG_STR_LEN );
snprintf( log_str, LOG_STR_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);
printh("-------------------log_clean RunTimer : %lf----------------\r\n",(double)(end-begin)/CLOCKS_PER_SEC);
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_STR_LEN );
switch (data->type)
{
case LOG_SHOW_CNT: //打印指定数量的日志
snprintf( log_str, LOG_STR_LEN, "SELECT * FROM better_log ORDER BY id DESC LIMIT %d;", data->param );
break;
case LOG_SHOW_LVL: //打印指定等级的日志
snprintf( log_str, LOG_STR_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_STR_LEN, "SELECT * FROM better_log WHERE LOG GLOB \'*%s*\' ORDER BY id DESC;",
data->log_show_str );
}
else
{
snprintf( log_str, LOG_STR_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_STR_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_fifo.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 )
{
memset( log_out_struct, 0, sizeof(_log_out_t) );
_log_out_t *log_data = log_out_struct;
log_data->module = module;
log_data->lvl = priority;
memcpy( log_data->log_out_str, va_str, LOG_STR_LEN );
_log_msg_send( LOG_OUT, log_data );
}
/* Interface functions -------------------------------------------------------*/
/* log打印输出函数. */
#define BLOG_FUNC(FUNCNAME,PRIORITY) \
void FUNCNAME(LOG_MODULE_E module, const char *format, ...) \
{ \
log_sys_t *log = log_sys; \
if (NULL == log) return; \
LOG_LOCK; \
memset( log_fifo_va_str, 0, LOG_STR_LEN ); \
va_list args; \
va_start(args, format); \
vsnprintf(log_fifo_va_str, LOG_STR_LEN, format, args); \
va_end(args); \
if ((1 << PRIORITY) & log->enable_lvl[LOG_MODE_STDOUT]) \
_log_out_std(module, PRIORITY, log_fifo_va_str); \
if (((1 << PRIORITY) & log->enable_lvl[LOG_MODE_FILE]) && log->db){ \
if (log_fifo.log_fifo_id) \
_log_out_db(module, PRIORITY, log_fifo_va_str); \
else \
_log_fifo_out(module, PRIORITY, log_fifo_va_str);} \
LOG_UNLOCK; \
}
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, ...)
{
log_sys_t *log = log_sys;
if (NULL == log) return;
LOG_OUT_LOCK;
memset( log_out_va_str, 0, LOG_STR_LEN );
va_list args;
va_start(args, format);
vsnprintf(log_out_va_str, LOG_STR_LEN, format, args);
va_end (args);
/* 串口log. */
if ((1 << priority) & log->enable_lvl[LOG_MODE_STDOUT])
_log_out_std(module, priority, log_out_va_str);
/* 判断是否是文件输出 */
if (((1 << priority) & log->enable_lvl[LOG_MODE_FILE]) && log->db)
_log_out_db(module, priority, log_out_va_str);
LOG_OUT_UNLOCK;
}
/* 打印日志函数 */
void log_show( int32_t show_cnt, LOG_LVL_E priority, const char *key_word )
{
memset( log_show_struct, 0, sizeof(_log_show_t) );
_log_show_t *log_data = log_show_struct;
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;
memcpy( log_data->log_show_str, key_word, LOG_STR_LEN );
}
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_STR_LEN);
log_out_va_str = XMALLOC(MTYPE_LOG, LOG_STR_LEN);
log_fifo_va_str = XMALLOC(MTYPE_LOG, LOG_STR_LEN);
log_out_struct = XMALLOC(MTYPE_LOG, sizeof(_log_out_t));
log_show_struct = XMALLOC(MTYPE_LOG, sizeof(_log_show_t));
log_sys = XMALLOC(MTYPE_LOG, sizeof(log_sys_t));
if (NULL == log_sys)
{
return E_MEM;
}
log = log_sys;
pthread_mutex_init(&log->log_mutex, NULL);
pthread_mutex_init(&log->log_out_mutex, NULL);
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);
}
}
/* 读取log fifo数据循环线程 */
void *_log_handle( void *arg )
{
_log_msg_t *recv_msg = NULL;
while(1)
{
if (fifo_read( log_fifo.log_fifo_id, (void **)&recv_msg ) != 0)
{
DBG( DBG_M_FIFO, "ERROR at fifo %d read!\r\n", log_fifo.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 );
}
fifo_push( log_fifo.log_fifo_id );
}
return NULL;
}
/* log线程初始化函数 */
int32_t _log_init_common( void )
{
struct sched_param param;
pthread_attr_t attr;
pthread_t pid;
int rv = 0;
/* 初始化log fifo */
log_fifo.log_fifo_id = fifo_create( LOG_DB_FIFO, 32 );
if (log_fifo.log_fifo_id < 0)
{
log_out( LOG_DEFAULT, LOG_LVL_ERR, "Open fifo " LOG_DB_FIFO " error." );
return E_ERROR;
}
pthread_mutex_init( &log_fifo.mutex, NULL );
/* 配置线程RR调度优先级50 */
pthread_attr_init( &attr );
param.sched_priority = 25;
pthread_attr_setschedpolicy( &attr, SCHED_RR );
pthread_attr_setschedparam( &attr, &param );
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;
}
int32_t log_handle_init( void )
{
LD_E_RETURN( DBG_M_DBG, _log_init_common() );
return E_NONE;
}
/************************ (C) COPYRIGHT LandPower ***** END OF FILE ****************/