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

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