#include "precompile.h"
#include "SpBase.h"
#include "app.h"
#include "sp_log.h"
#include "sp_dbg_export.h"
#include "sp_svc.h"
#include "sp_cfg.h"
#include "sp_env.h"
#include "fileutil.h"
#include "strutil.h"
#ifdef _WIN32
#include "SpShellConsole.h"
#endif //_WIN32
#define MAX_LOGFILE_SIZE (1<<20)
typedef struct log_t {
SYSTEMTIME tm_start;
FILE *fp;
int file_seq;// non-zero when file is already exist
int last_error;
sp_log_daemon_t *log_daemon;
}log_t;
static const char *str_types[] = {
"Nil",
"Evt",
"Wrn",
"Err",
"Dbg",
};
static const char *str_severity[] = {
"Nil",
"low",
"mid",
"hgh",
};
static BOOL __log_need_change(log_t *log, LPSYSTEMTIME now)
{
assert(now);
/* if error occurs, may be Hard Disk broken or space is run out. so no need to change file */
if (log->last_error)
return FALSE;
/* fp currently is null, when system start */
if (!log->fp)
return TRUE;
/* a new day come */
if (log->tm_start.wYear != now->wYear ||
log->tm_start.wMonth != now->wMonth ||
log->tm_start.wDay != now->wDay)
return TRUE;
/* file size has exceed MAX_LOGFILE_SIZE */
if (ftell(log->fp) > MAX_LOGFILE_SIZE) {
return TRUE;
}
/* 10 minutes pass */
{
int diff = (now->wHour - log->tm_start.wHour) * 3600;
diff += (now->wMinute - log->tm_start.wMinute) * 60;
diff += (now->wSecond - log->tm_start.wSecond);
if (diff >= 600) {
return TRUE;
}
}
return FALSE;
}
static const char *get_temp_path(char *buf, LPSYSTEMTIME now, int seq)
{
sp_env_t *env = sp_get_env();
sp_cfg_t *cfg = env->cfg;
sp_cfg_root_ini_t *root_cfg = cfg->root_ini;
const char *dir = root_cfg->ref_syslog_path;
if (seq) {
sprintf(buf, "%s" SPLIT_SLASH_STR "%08X_%02d-%02d-%02d_%02d-%02d-%02d(%03d)", dir,
cfg->install_ini->current_startup_time,
now->wYear, now->wMonth, now->wDay,
now->wHour, now->wMinute, now->wSecond, seq%1000);
} else {
sprintf(buf, "%s" SPLIT_SLASH_STR "%08X_%02d-%02d-%02d_%02d-%02d-%02d", dir,
cfg->install_ini->current_startup_time,
now->wYear, now->wMonth, now->wDay,
now->wHour, now->wMinute, now->wSecond);
}
return buf;
}
static const char *new_tmp_path(char *buf, LPSYSTEMTIME now, int *file_seq)
{
int seq = 0;
int i;
const int max_tries = 1000;
for (i = 0; i < max_tries; ++i) {
get_temp_path(buf, now, seq);
strcat(buf, ".xml");
if (ExistsFileA(buf)) {
seq++;
} else {
break;
}
}
if (i == max_tries) {
return NULL;
}
//buf[strlen(buf) - 4] = 0;
*file_seq = seq;
return buf;
}
static BOOL rename_file1(LPSYSTEMTIME tm_start, int file_seq)
{
char x[MAX_PATH];
char y[MAX_PATH];
get_temp_path(x, tm_start, file_seq);
strcpy(y, x);
strcat(y, ".xml");
return MoveFileExA(x, y, MOVEFILE_REPLACE_EXISTING);
}
static void __log_change_file(log_t *log, LPSYSTEMTIME now)
{
char tmp[MAX_PATH];
if (log->fp) {
fflush(log->fp);
fclose(log->fp);
log->fp = NULL;
}
if (!new_tmp_path(tmp, now, &log->file_seq)) {
log->last_error = -1;
sp_dbg_warn("cannot create log tmp file");
return;
}
log->fp = fopen(tmp, "wb");
if (!log->fp) {
log->last_error = -1;
sp_dbg_warn("cannot fopen log tmp file %s", tmp);
return;
} else {
memcpy(&log->tm_start, now, sizeof(SYSTEMTIME));
}
}
static void __log_write_record(log_t *log,
u__int64_t log_id,
LPSYSTEMTIME tm,
int log_type,
u__int64_t prev_rsn,
u__int64_t curr_rsn,
int original_rsn_type,
int rsn_depth,
int log_severity,
int log_sys_error,
int log_usr_error,
const char *module,
const char *entity,
const char *msg)
{
int n;
n = fprintf(log->fp,
//"%s\r\n",
"%s\r\n",
log_id,
tm->wHour, tm->wMinute, tm->wSecond,
log_type,
/*curr_rsn,
prev_rsn,
original_rsn_type,
rsn_depth, */
log_severity,
log_sys_error,
log_usr_error,
module,
entity,
msg);
if (n < 0) {
log->last_error = -1;
fclose(log->fp);
log->fp = NULL;
sp_dbg_warn("write log record failed!");
}
}
static void on_log(void *inst,
int nsub,
u__int64_t *sub,
int client_id,
int log_epid,
int client_instance_id,
u__int64_t log_id,
u__int64_t prev_rsn,
u__int64_t curr_rsn,
int original_rsn_type,
int rsn_depth,
unsigned int log_time,
int log_type,
int log_severity,
int log_sys_error,
int log_usr_error,
int param_cnt,
int *params,
const char *msg,
void *user_data)
{
sp_env_t *env = sp_get_env();
sp_cfg_t *cfg = env->cfg;
sp_cfg_shell_entity_t *ent = sp_cfg_get_entity_by_idx(cfg, client_id);
sp_cfg_shell_module_t *mod = ent ? ent->mod : sp_cfg_get_module_by_idx(cfg, log_epid);
log_t *log = (log_t*)user_data;
app_t *app = get_app_instance();
#ifdef _WIN32
// 在调试控制台显示已订阅实体日志
if (app->pConsole != NULL && app->pConsole->HasSubLogCnn()) {
app->pConsole->OutputLog(client_id, ent->name, msg);
}
// 在启动画面显示日志
if (log_type == 1) // Log_Event
sp_gui_show_running_info(app->bsc_gui, CSimpleString::Format("[%s] I:{%s}(uc:0x%X)", ent->name, msg, log_usr_error), 0);
else if (log_type == 2) // Log_Warning
sp_gui_show_running_info(app->bsc_gui, CSimpleString::Format("[%s] W:{%s}(sc:0x%X, uc:0x%X)", ent->name, msg, log_sys_error, log_usr_error), 0);
else if (log_type == 3) // Log_Error
sp_gui_show_running_info(app->bsc_gui, CSimpleString::Format("[%s] E:{%s}(sc:0x%X, uc:0x%X)", ent->name, msg, log_sys_error, log_usr_error), 0);
else if (log_type == 4 || log_type == 0) // Log_Debug
sp_gui_show_running_info(app->bsc_gui, CSimpleString::Format("[%s] D:{%s}", ent->name, msg), 0);
#endif //_WIN32
// 输出到文件
SYSTEMTIME st;
y2k_to_localtime(log_time, &st);
if (__log_need_change(log, &st)) {
__log_change_file(log, &st);
}
if (!log->last_error && log->fp) {
LARGE_INTEGER t;
char *escape = str_xml_escape(msg);
t.QuadPart = log_id;
__log_write_record(log,
log_id, &st, log_type,
prev_rsn, curr_rsn, original_rsn_type, rsn_depth,
log_severity, log_sys_error, log_usr_error,
mod->name, ent ? ent->name : "$Anonymous$", escape);
FREE(escape);
}
}
// user send flush command
static void on_flush(void *inst,
int client_id,
LPSYSTEMTIME lst,
void *user_data)
{
log_t *log = (log_t*)user_data;
if (log->fp) {
fflush(log->fp);
/*fclose(log->fp);
log->fp = NULL;*/
}
}
static void on_timeout_interval(void *inst, LPSYSTEMTIME lst, void *user_data)
{
log_t *log = (log_t*)user_data;
if (log->fp) {
if (__log_need_change(log, lst)) {
fclose(log->fp);
log->fp = NULL;
} else {
fflush(log->fp);
}
}
}
int log_create(sp_svc_t *svc, log_t **p_log)
{
log_t *log = ZALLOC_T(log_t);
sp_env_t *env = sp_get_env();
sp_cfg_t *cfg = env->cfg;
sp_cfg_root_ini_t *root_cfg = cfg->root_ini;
const char *dir = root_cfg->ref_syslog_path;
int rc;
rc = sp_log_daemon_create(&on_log, &on_flush, &on_timeout_interval, log, svc, &log->log_daemon);
if (rc == 0) {
*p_log = log;
} else {
free(log);
}
return rc;
}
int log_destroy(log_t *log)
{
int rc;
rc = sp_log_daemon_destroy(log->log_daemon);
if (rc == 0) {
if (log->fp) {
fflush(log->fp);
fclose(log->fp);
log->fp = NULL;
}
free(log);
}
return 0;
}