libfastcommon/src/logger.c

1347 lines
32 KiB
C

/*
* Copyright (c) 2020 YuQing <384681@qq.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the Lesser GNU General Public License, version 3
* or later ("LGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the Lesser GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <pthread.h>
#include "shared_func.h"
#include "pthread_func.h"
#include "sched_thread.h"
#include "logger.h"
#ifndef LINE_MAX
#define LINE_MAX 2048
#endif
#define LOG_BUFF_SIZE 64 * 1024
#define NEED_COMPRESS_LOG(flags) ((flags & LOG_COMPRESS_FLAGS_ENABLED) != 0)
#define COMPRESS_IN_NEW_THREAD(flags) ((flags & LOG_COMPRESS_FLAGS_NEW_THREAD) != 0)
#define GZIP_EXT_NAME_STR ".gz"
#define GZIP_EXT_NAME_LEN (sizeof(GZIP_EXT_NAME_STR) - 1)
LogContext g_log_context = {LOG_INFO, STDERR_FILENO, NULL};
static int log_fsync(LogContext *pContext, const bool bNeedLock);
static int check_and_mk_log_dir(const char *base_path)
{
char data_path[MAX_PATH_SIZE];
snprintf(data_path, sizeof(data_path), "%s/logs", base_path);
if (!fileExists(data_path))
{
if (mkdir(data_path, 0755) != 0)
{
fprintf(stderr, "mkdir \"%s\" fail, " \
"errno: %d, error info: %s\n", \
data_path, errno, STRERROR(errno));
return errno != 0 ? errno : EPERM;
}
}
return 0;
}
int log_init()
{
if (g_log_context.log_buff != NULL)
{
fprintf(stderr, "file: "__FILE__", line: %d, "
"g_log_context already inited\n", __LINE__);
return 0;
}
return log_init_ex(&g_log_context);
}
int log_init2()
{
int result;
if ((result=log_init()) != 0) {
return result;
}
log_take_over_stderr();
log_take_over_stdout();
return 0;
}
int log_init_ex(LogContext *pContext)
{
int result;
memset(pContext, 0, sizeof(LogContext));
pContext->log_level = LOG_INFO;
pContext->log_fd = STDERR_FILENO;
pContext->time_precision = LOG_TIME_PRECISION_SECOND;
pContext->compress_log_days_before = 1;
strcpy(pContext->rotate_time_format, "%Y%m%d_%H%M%S");
pContext->log_buff = (char *)malloc(LOG_BUFF_SIZE);
if (pContext->log_buff == NULL)
{
fprintf(stderr, "malloc %d bytes fail, " \
"errno: %d, error info: %s\n", \
LOG_BUFF_SIZE, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
pContext->pcurrent_buff = pContext->log_buff;
if ((result=init_pthread_lock(&(pContext->log_thread_lock))) != 0)
{
return result;
}
return 0;
}
static int log_print_header(LogContext *pContext)
{
int result;
if (!pContext->use_file_write_lock)
{
if ((result=file_write_lock(pContext->log_fd)) != 0)
{
return result;
}
}
pContext->current_size = lseek(pContext->log_fd, 0, SEEK_END);
if (pContext->current_size < 0)
{
result = errno != 0 ? errno : EACCES;
fprintf(stderr, "lseek file \"%s\" fail, " \
"errno: %d, error info: %s\n", \
pContext->log_filename, result, STRERROR(result));
}
else {
result = 0;
if (pContext->current_size == 0) {
pContext->print_header_callback(pContext);
}
}
if (!pContext->use_file_write_lock)
{
file_unlock(pContext->log_fd);
}
return result;
}
static int log_open(LogContext *pContext)
{
int result;
if ((pContext->log_fd = open(pContext->log_filename, O_WRONLY | O_CREAT |
O_APPEND | O_CLOEXEC | pContext->fd_flags, 0644)) < 0)
{
fprintf(stderr, "open log file \"%s\" to write fail, " \
"errno: %d, error info: %s\n", \
pContext->log_filename, errno, STRERROR(errno));
pContext->log_fd = STDERR_FILENO;
return errno != 0 ? errno : EACCES;
}
if (pContext->use_file_write_lock) {
if ((result=file_try_write_lock(pContext->log_fd)) != 0) {
close(pContext->log_fd);
pContext->log_fd = STDERR_FILENO;
return result;
}
}
if (pContext->take_over_stderr) {
if (dup2(pContext->log_fd, STDERR_FILENO) < 0) {
fprintf(stderr, "file: "__FILE__", line: %d, "
"call dup2 fail, errno: %d, error info: %s\n",
__LINE__, errno, STRERROR(errno));
}
}
if (pContext->take_over_stdout) {
if (dup2(pContext->log_fd, STDOUT_FILENO) < 0) {
fprintf(stderr, "file: "__FILE__", line: %d, "
"call dup2 fail, errno: %d, error info: %s\n",
__LINE__, errno, STRERROR(errno));
}
}
pContext->current_size = lseek(pContext->log_fd, 0, SEEK_END);
if (pContext->current_size < 0)
{
fprintf(stderr, "lseek file \"%s\" fail, " \
"errno: %d, error info: %s\n", \
pContext->log_filename, errno, STRERROR(errno));
return errno != 0 ? errno : EACCES;
}
if (pContext->current_size == 0 && pContext->print_header_callback != NULL)
{
log_print_header(pContext);
}
return 0;
}
int log_reopen_ex(LogContext *pContext)
{
if (*(pContext->log_filename) == '\0')
{
return ENOENT;
}
if (pContext->log_fd >= 0 && pContext->log_fd != STDERR_FILENO)
{
close(pContext->log_fd);
}
return log_open(pContext);
}
int log_set_prefix_ex(LogContext *pContext, const char *base_path,
const char *filename_prefix)
{
int result;
char log_filename[MAX_PATH_SIZE];
if ((result=check_and_mk_log_dir(base_path)) != 0)
{
return result;
}
snprintf(log_filename, MAX_PATH_SIZE, "%s/logs/%s.log",
base_path, filename_prefix);
return log_set_filename_ex(pContext, log_filename);
}
int log_set_filename_ex(LogContext *pContext, const char *log_filename)
{
if (log_filename == NULL || *log_filename == '\0')
{
fprintf(stderr, "file: "__FILE__", line: %d, "
"log_filename is NULL or empty!\n", __LINE__);
return EINVAL;
}
if (*(pContext->log_filename) == '\0')
{
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s", log_filename);
return log_open(pContext);
}
if (strcmp(log_filename, pContext->log_filename) == 0)
{
return 0;
}
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s", log_filename);
return log_reopen_ex(pContext);
}
void log_set_cache_ex(LogContext *pContext, const bool bLogCache)
{
pContext->log_to_cache = bLogCache;
}
void log_set_use_file_write_lock_ex(LogContext *pContext, const bool use_lock)
{
pContext->use_file_write_lock = use_lock;
}
void log_set_time_precision(LogContext *pContext, const int time_precision)
{
pContext->time_precision = time_precision;
}
void log_set_rotate_time_format(LogContext *pContext, const char *time_format)
{
snprintf(pContext->rotate_time_format,
sizeof(pContext->rotate_time_format),
"%s", time_format);
}
void log_set_keep_days(LogContext *pContext, const int keep_days)
{
pContext->keep_days = keep_days;
}
void log_set_header_callback(LogContext *pContext, LogHeaderCallback header_callback)
{
pContext->print_header_callback = header_callback;
if (pContext->print_header_callback != NULL)
{
int64_t current_size;
pthread_mutex_lock(&(pContext->log_thread_lock));
current_size = pContext->current_size;
pthread_mutex_unlock(&(pContext->log_thread_lock));
if (current_size == 0)
{
log_print_header(pContext);
}
}
}
void log_take_over_stderr_ex(LogContext *pContext)
{
pContext->take_over_stderr = true;
}
void log_take_over_stdout_ex(LogContext *pContext)
{
pContext->take_over_stdout = true;
}
void log_set_compress_log_flags_ex(LogContext *pContext, const short flags)
{
pContext->compress_log_flags = flags;
}
void log_set_compress_log_days_before_ex(LogContext *pContext, const int days_before)
{
if (days_before > 0)
{
pContext->compress_log_days_before = days_before;
}
else
{
pContext->compress_log_days_before = 1;
}
}
void log_set_fd_flags(LogContext *pContext, const int flags)
{
pContext->fd_flags = flags;
}
void log_destroy_ex(LogContext *pContext)
{
if (pContext->log_fd >= 0 && pContext->log_fd != STDERR_FILENO)
{
log_fsync(pContext, true);
close(pContext->log_fd);
pContext->log_fd = STDERR_FILENO;
pthread_mutex_destroy(&pContext->log_thread_lock);
}
if (pContext->log_buff != NULL)
{
free(pContext->log_buff);
pContext->log_buff = NULL;
pContext->pcurrent_buff = NULL;
}
}
int log_sync_func(void *args)
{
if (args == NULL)
{
return EINVAL;
}
return log_fsync((LogContext *)args, true);
}
int log_notify_rotate(void *args)
{
if (args == NULL)
{
return EINVAL;
}
((LogContext *)args)->rotate_immediately = true;
return 0;
}
static int log_delete_old_file(LogContext *pContext,
const char *old_filename)
{
char full_filename[MAX_PATH_SIZE + 128];
if (NEED_COMPRESS_LOG(pContext->compress_log_flags))
{
snprintf(full_filename, sizeof(full_filename), "%s%s",
old_filename, GZIP_EXT_NAME_STR);
}
else
{
snprintf(full_filename, sizeof(full_filename), "%s", old_filename);
}
if (unlink(full_filename) != 0)
{
if (errno != ENOENT)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"unlink %s fail, errno: %d, error info: %s\n", \
__LINE__, full_filename, errno, STRERROR(errno));
}
else if (NEED_COMPRESS_LOG(pContext->compress_log_flags))
{
unlink(old_filename);
}
return errno != 0 ? errno : EPERM;
}
return 0;
}
static int log_get_prefix_len(LogContext *pContext, int *prefix_len)
{
char *p;
if (*(pContext->log_filename) == '\0' || \
*(pContext->rotate_time_format) == '\0')
{
*prefix_len = 0;
return EINVAL;
}
p = pContext->rotate_time_format + strlen(pContext->rotate_time_format) - 1;
while (p > pContext->rotate_time_format)
{
if (*(p-1) != '%')
{
break;
}
if (*p == 'd' || *p == 'm' || *p == 'Y' || *p == 'y')
{
break;
}
p -= 2;
}
*prefix_len = (p - pContext->rotate_time_format) + 1;
if (*prefix_len == 0)
{
return EINVAL;
}
return 0;
}
struct log_filename_array {
char **filenames;
int count;
int size;
};
static int log_check_filename_array_size(struct log_filename_array *
filename_array)
{
char **new_filenames;
int new_size;
int bytes;
if (filename_array->size > filename_array->count)
{
return 0;
}
new_size = filename_array->size == 0 ? 8 : filename_array->size * 2;
bytes = sizeof(char *) * new_size;
new_filenames = (char **)malloc(bytes);
if (new_filenames == NULL)
{
fprintf(stderr, "file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s\n",
__LINE__, bytes, errno, STRERROR(errno));
return errno != 0 ? errno : ENOMEM;
}
if (filename_array->count > 0)
{
memcpy(new_filenames, filename_array->filenames,
sizeof(char *) * filename_array->count);
}
if (filename_array->filenames != NULL)
{
free(filename_array->filenames);
}
filename_array->filenames = new_filenames;
filename_array->size = new_size;
return 0;
}
static void log_free_filename_array(struct log_filename_array *
filename_array)
{
int i;
if (filename_array->filenames == NULL)
{
return;
}
for (i=0; i<filename_array->count; i++)
{
free(filename_array->filenames[i]);
}
free(filename_array->filenames);
filename_array->filenames = NULL;
filename_array->size = 0;
filename_array->count = 0;
}
static void log_get_file_path(LogContext *pContext, char *log_filepath)
{
char *p;
p = strrchr(pContext->log_filename, '/');
if (p == NULL)
{
*log_filepath = '.';
*(log_filepath + 1) = '/';
*(log_filepath + 2) = '\0';
}
else
{
int path_len;
path_len = (p - pContext->log_filename) + 1;
memcpy(log_filepath, pContext->log_filename, path_len);
*(log_filepath + path_len) = '\0';
}
}
static int log_get_matched_files(LogContext *pContext,
const int prefix_len, const int days_before,
struct log_filename_array *filename_array)
{
char rotate_time_format_prefix[32];
char log_filepath[MAX_PATH_SIZE];
char filename_prefix[MAX_PATH_SIZE + 32];
int prefix_filename_len;
int result;
int len;
char *p;
char *log_filename;
char *filename;
DIR *dir;
#ifndef OS_LINUX
struct dirent ent;
#endif
struct dirent *pEntry;
time_t the_time;
struct tm tm;
filename_array->filenames = NULL;
filename_array->count = 0;
filename_array->size = 0;
p = strrchr(pContext->log_filename, '/');
if (p == NULL)
{
*log_filepath = '.';
*(log_filepath + 1) = '/';
*(log_filepath + 2) = '\0';
log_filename = pContext->log_filename;
}
else
{
int path_len;
path_len = (p - pContext->log_filename) + 1;
memcpy(log_filepath, pContext->log_filename, path_len);
*(log_filepath + path_len) = '\0';
log_filename = p + 1;
}
memcpy(rotate_time_format_prefix, pContext->rotate_time_format, prefix_len);
*(rotate_time_format_prefix + prefix_len) = '\0';
dir = opendir(log_filepath);
if (dir == NULL)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"opendir %s fail, errno: %d, error info: %s\n", \
__LINE__, log_filepath, errno, STRERROR(errno));
return errno != 0 ? errno : ENOENT;
}
result = 0;
the_time = get_current_time() - days_before * 86400;
localtime_r(&the_time, &tm);
memset(filename_prefix, 0, sizeof(filename_prefix));
len = strlen(log_filename);
memcpy(filename_prefix, log_filename, len);
*(filename_prefix + len++) = '.';
strftime(filename_prefix + len, sizeof(filename_prefix) - len,
rotate_time_format_prefix, &tm);
prefix_filename_len = strlen(filename_prefix);
#ifndef OS_LINUX
while (readdir_r(dir, &ent, &pEntry) == 0)
#else
while ((pEntry=readdir(dir)) != NULL)
#endif
{
if (pEntry == NULL)
{
break;
}
if ((int)strlen(pEntry->d_name) >= prefix_filename_len &&
memcmp(pEntry->d_name, filename_prefix,
prefix_filename_len) == 0)
{
if ((result=log_check_filename_array_size(filename_array)) != 0)
{
break;
}
filename = strdup(pEntry->d_name);
if (filename == NULL)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"strdup %s fail, errno: %d, error info: %s\n", \
__LINE__, pEntry->d_name, errno, STRERROR(errno));
break;
}
filename_array->filenames[filename_array->count] = filename;
filename_array->count++;
}
}
closedir(dir);
return result;
}
static int log_delete_matched_old_files(LogContext *pContext,
const int prefix_len)
{
struct log_filename_array filename_array;
char log_filepath[MAX_PATH_SIZE];
char full_filename[MAX_PATH_SIZE + 32];
int result;
int i;
if ((result=log_get_matched_files(pContext,
prefix_len, pContext->keep_days + 1,
&filename_array)) != 0)
{
return result;
}
log_get_file_path(pContext, log_filepath);
for (i=0; i<filename_array.count; i++)
{
snprintf(full_filename, sizeof(full_filename), "%s%s",
log_filepath, filename_array.filenames[i]);
if (unlink(full_filename) != 0)
{
if (errno != ENOENT)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"unlink %s fail, errno: %d, error info: %s\n", \
__LINE__, full_filename, errno, STRERROR(errno));
result = errno != 0 ? errno : EPERM;
break;
}
}
}
log_free_filename_array(&filename_array);
return result;
}
int log_delete_old_files(void *args)
{
LogContext *pContext;
char old_filename[MAX_PATH_SIZE + 32];
int prefix_len;
int len;
int result;
struct tm tm;
if (args == NULL)
{
return EINVAL;
}
pContext = (LogContext *)args;
if (pContext->keep_days <= 0) {
return 0;
}
if ((result=log_get_prefix_len(pContext, &prefix_len)) != 0)
{
return result;
}
if (prefix_len == (int)strlen(pContext->rotate_time_format))
{
time_t the_time;
the_time = get_current_time() - pContext->keep_days * 86400;
while (1) {
the_time -= 86400;
localtime_r(&the_time, &tm);
memset(old_filename, 0, sizeof(old_filename));
len = strlen(pContext->log_filename);
memcpy(old_filename, pContext->log_filename, len);
*(old_filename + len++) = '.';
strftime(old_filename + len, sizeof(old_filename) - len,
pContext->rotate_time_format, &tm);
if ((result=log_delete_old_file(pContext, old_filename)) != 0)
{
if (result != ENOENT)
{
return result;
}
break;
}
}
return 0;
}
else
{
return log_delete_matched_old_files(pContext, prefix_len);
}
}
static void *log_gzip_func(void *args)
{
LogContext *pContext;
char cmd[MAX_PATH_SIZE + 128];
struct log_filename_array filename_array;
char log_filepath[MAX_PATH_SIZE];
char full_filename[MAX_PATH_SIZE + 32];
char output[512];
int prefix_len;
int result;
int i;
pContext = (LogContext *)args;
if (log_get_prefix_len(pContext, &prefix_len) != 0)
{
return NULL;
}
if (log_get_matched_files(pContext, prefix_len,
pContext->compress_log_days_before, &filename_array) != 0)
{
return NULL;
}
log_get_file_path(pContext, log_filepath);
for (i=0; i<filename_array.count; i++)
{
int len;
len = strlen(filename_array.filenames[i]);
if ((len > GZIP_EXT_NAME_LEN) && memcmp(filename_array.filenames[i]
+ len - GZIP_EXT_NAME_LEN, GZIP_EXT_NAME_STR,
GZIP_EXT_NAME_LEN) == 0)
{
continue;
}
snprintf(full_filename, sizeof(full_filename), "%s%s",
log_filepath, filename_array.filenames[i]);
snprintf(cmd, sizeof(cmd), "%s %s",
get_gzip_command_filename(), full_filename);
result = getExecResult(cmd, output, sizeof(output));
if (result != 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, "
"exec command \"%s\" fail, "
"errno: %d, error info: %s",
__LINE__, cmd, result, STRERROR(result));
break;
}
if (*output != '\0')
{
fprintf(stderr, "file: "__FILE__", line: %d, "
"exec command \"%s\", output: %s",
__LINE__, cmd, output);
}
}
log_free_filename_array(&filename_array);
return NULL;
}
static void log_gzip(LogContext *pContext)
{
if (COMPRESS_IN_NEW_THREAD(pContext->compress_log_flags))
{
int result;
pthread_t tid;
pthread_attr_t thread_attr;
if ((result=init_pthread_attr(&thread_attr, 0) != 0))
{
return;
}
if ((result=pthread_create(&tid, &thread_attr,
log_gzip_func, pContext)) != 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"create thread failed, " \
"errno: %d, error info: %s\n", \
__LINE__, result, STRERROR(result));
}
pthread_attr_destroy(&thread_attr);
}
else
{
log_gzip_func(pContext);
}
}
int log_rotate(LogContext *pContext)
{
struct tm tm;
time_t current_time;
int len;
int result;
char old_filename[MAX_PATH_SIZE + 32];
bool exist;
if (*(pContext->log_filename) == '\0')
{
return ENOENT;
}
close(pContext->log_fd);
current_time = get_current_time();
if (tm.tm_hour == 0 && tm.tm_min <= 1)
{
if (strstr(pContext->rotate_time_format, "%H") == NULL
&& strstr(pContext->rotate_time_format, "%M") == NULL
&& strstr(pContext->rotate_time_format, "%S") == NULL)
{
current_time -= 120;
}
}
localtime_r(&current_time, &tm);
memset(old_filename, 0, sizeof(old_filename));
len = strlen(pContext->log_filename);
memcpy(old_filename, pContext->log_filename, len);
*(old_filename + len++) = '.';
strftime(old_filename + len, sizeof(old_filename) - len,
pContext->rotate_time_format, &tm);
if (access(old_filename, F_OK) == 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"file: %s already exist, rotate file fail\n",
__LINE__, old_filename);
exist = true;
}
else if (rename(pContext->log_filename, old_filename) != 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"rename %s to %s fail, errno: %d, error info: %s\n", \
__LINE__, pContext->log_filename, old_filename, \
errno, STRERROR(errno));
exist = false;
}
else
{
exist = true;
}
result = log_open(pContext);
if (exist && NEED_COMPRESS_LOG(pContext->compress_log_flags))
{
log_gzip(pContext);
}
return result;
}
static int log_check_rotate(LogContext *pContext)
{
if (pContext->log_fd == STDERR_FILENO)
{
if (pContext->current_size > 0)
{
pContext->current_size = 0;
}
return ENOENT;
}
if (pContext->rotate_immediately)
{
pContext->rotate_immediately = false;
return log_rotate(pContext);
}
return 0;
}
static int log_fsync(LogContext *pContext, const bool bNeedLock)
{
int result;
int lock_res;
int write_bytes;
int written;
if (pContext->pcurrent_buff - pContext->log_buff == 0)
{
if (!pContext->rotate_immediately)
{
return 0;
}
else
{
if (bNeedLock)
{
pthread_mutex_lock(&(pContext->log_thread_lock));
}
result = log_check_rotate(pContext);
if (bNeedLock)
{
pthread_mutex_unlock(&(pContext->log_thread_lock));
}
return result;
}
}
if (bNeedLock && ((lock_res=pthread_mutex_lock( \
&(pContext->log_thread_lock))) != 0))
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"call pthread_mutex_lock fail, " \
"errno: %d, error info: %s\n", \
__LINE__, lock_res, STRERROR(lock_res));
}
write_bytes = pContext->pcurrent_buff - pContext->log_buff;
pContext->current_size += write_bytes;
if (pContext->rotate_size > 0)
{
if (pContext->current_size > pContext->rotate_size)
{
pContext->rotate_immediately = true;
log_check_rotate(pContext);
}
}
result = 0;
written = write(pContext->log_fd, pContext->log_buff, write_bytes);
pContext->pcurrent_buff = pContext->log_buff;
if (written != write_bytes)
{
result = errno != 0 ? errno : EIO;
fprintf(stderr, "file: "__FILE__", line: %d, "
"pid: %d, call write fail, fd: %d, errno: %d, error info: %s\n",
__LINE__, getpid(), pContext->log_fd, result, STRERROR(result));
}
if (pContext->rotate_immediately)
{
log_check_rotate(pContext);
}
if (bNeedLock && ((lock_res=pthread_mutex_unlock( \
&(pContext->log_thread_lock))) != 0))
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"call pthread_mutex_unlock fail, " \
"errno: %d, error info: %s\n", \
__LINE__, lock_res, STRERROR(lock_res));
}
return result;
}
static void doLogEx(LogContext *pContext, struct timeval *tv, \
const char *caption, const char *text, const int text_len, \
const bool bNeedSync, const bool bNeedLock)
{
struct tm tm;
int time_fragment;
int buff_len;
int result;
if ((pContext->time_precision == LOG_TIME_PRECISION_SECOND)
|| (pContext->time_precision == LOG_TIME_PRECISION_NONE))
{
time_fragment = 0;
}
else
{
if (pContext->time_precision == LOG_TIME_PRECISION_MSECOND)
{
time_fragment = tv->tv_usec / 1000;
}
else
{
time_fragment = tv->tv_usec;
}
}
if (bNeedLock && (result=pthread_mutex_lock(&pContext->log_thread_lock)) != 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"call pthread_mutex_lock fail, " \
"errno: %d, error info: %s\n", \
__LINE__, result, STRERROR(result));
}
if (text_len + 64 > LOG_BUFF_SIZE)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"log buff size: %d < log text length: %d\n", \
__LINE__, LOG_BUFF_SIZE, text_len + 64);
if (bNeedLock)
{
pthread_mutex_unlock(&(pContext->log_thread_lock));
}
return;
}
if ((pContext->pcurrent_buff - pContext->log_buff) + text_len + 64 \
> LOG_BUFF_SIZE)
{
log_fsync(pContext, false);
}
if (pContext->time_precision != LOG_TIME_PRECISION_NONE)
{
localtime_r(&tv->tv_sec, &tm);
if (pContext->time_precision == LOG_TIME_PRECISION_SECOND)
{
buff_len = sprintf(pContext->pcurrent_buff, \
"[%04d-%02d-%02d %02d:%02d:%02d] ", \
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
else
{
buff_len = sprintf(pContext->pcurrent_buff, \
"[%04d-%02d-%02d %02d:%02d:%02d.%03d] ", \
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, \
tm.tm_hour, tm.tm_min, tm.tm_sec, time_fragment);
}
pContext->pcurrent_buff += buff_len;
}
if (caption != NULL)
{
buff_len = strlen(caption);
memcpy(pContext->pcurrent_buff, caption, buff_len);
pContext->pcurrent_buff += buff_len;
*pContext->pcurrent_buff++ = ' ';
*pContext->pcurrent_buff++ = '-';
*pContext->pcurrent_buff++ = ' ';
}
memcpy(pContext->pcurrent_buff, text, text_len);
pContext->pcurrent_buff += text_len;
*pContext->pcurrent_buff++ = '\n';
if (!pContext->log_to_cache || bNeedSync)
{
log_fsync(pContext, false);
}
if (bNeedLock && (result=pthread_mutex_unlock(&(pContext->log_thread_lock))) != 0)
{
fprintf(stderr, "file: "__FILE__", line: %d, " \
"call pthread_mutex_unlock fail, " \
"errno: %d, error info: %s\n", \
__LINE__, result, STRERROR(result));
}
}
void log_it_ex2(LogContext *pContext, const char *caption, \
const char *text, const int text_len, \
const bool bNeedSync, const bool bNeedLock)
{
struct timeval tv;
if (pContext->time_precision == LOG_TIME_PRECISION_SECOND)
{
tv.tv_sec = get_current_time();
tv.tv_usec = 0;
}
else if (pContext->time_precision != LOG_TIME_PRECISION_NONE)
{
gettimeofday(&tv, NULL);
}
doLogEx(pContext, &tv, caption, text, text_len, bNeedSync, bNeedLock);
}
void log_it_ex1(LogContext *pContext, const int priority, \
const char *text, const int text_len)
{
bool bNeedSync;
char *caption;
if (pContext->log_level < priority)
{
return;
}
switch (priority)
{
case LOG_DEBUG:
bNeedSync = true;
caption = "DEBUG";
break;
case LOG_INFO:
bNeedSync = true;
caption = "INFO";
break;
case LOG_NOTICE:
bNeedSync = false;
caption = "NOTICE";
break;
case LOG_WARNING:
bNeedSync = false;
caption = "WARNING";
break;
case LOG_ERR:
bNeedSync = false;
caption = "ERROR";
break;
case LOG_CRIT:
bNeedSync = true;
caption = "CRIT";
break;
case LOG_ALERT:
bNeedSync = true;
caption = "ALERT";
break;
case LOG_EMERG:
bNeedSync = true;
caption = "EMERG";
break;
default:
bNeedSync = false;
caption = "UNKOWN";
break;
}
log_it_ex2(pContext, caption, text, text_len, bNeedSync, true);
}
void log_it_ex(LogContext *pContext, const int priority, const char *format, ...)
{
bool bNeedSync;
char text[LINE_MAX];
char *caption;
int len;
va_list ap;
if (pContext->log_level < priority)
{
return;
}
va_start(ap, format);
len = vsnprintf(text, sizeof(text), format, ap);
va_end(ap);
if (len >= sizeof(text))
{
len = sizeof(text) - 1;
}
switch (priority)
{
case LOG_DEBUG:
bNeedSync = true;
caption = "DEBUG";
break;
case LOG_INFO:
bNeedSync = true;
caption = "INFO";
break;
case LOG_NOTICE:
bNeedSync = false;
caption = "NOTICE";
break;
case LOG_WARNING:
bNeedSync = false;
caption = "WARNING";
break;
case LOG_ERR:
bNeedSync = false;
caption = "ERROR";
break;
case LOG_CRIT:
bNeedSync = true;
caption = "CRIT";
break;
case LOG_ALERT:
bNeedSync = true;
caption = "ALERT";
break;
case LOG_EMERG:
bNeedSync = true;
caption = "EMERG";
break;
default:
bNeedSync = false;
caption = "UNKOWN";
break;
}
log_it_ex2(pContext, caption, text, len, bNeedSync, true);
}
#define _DO_LOG(pContext, priority, caption, bNeedSync) \
char text[LINE_MAX]; \
int len; \
\
if (pContext->log_level < priority) \
{ \
return; \
} \
\
{ \
va_list ap; \
va_start(ap, format); \
len = vsnprintf(text, sizeof(text), format, ap); \
va_end(ap); \
if (len >= sizeof(text)) \
{ \
len = sizeof(text) - 1; \
} \
} \
\
log_it_ex2(pContext, caption, text, len, bNeedSync, true);
void logEmergEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_EMERG, "EMERG", true)
}
void logAlertEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_ALERT, "ALERT", true)
}
void logCritEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_CRIT, "CRIT", true)
}
void logErrorEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_ERR, "ERROR", false)
}
void logWarningEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_WARNING, "WARNING", false)
}
void logNoticeEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_NOTICE, "NOTICE", false)
}
void logInfoEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_INFO, "INFO", false)
}
void logDebugEx(LogContext *pContext, const char *format, ...)
{
_DO_LOG(pContext, LOG_DEBUG, "DEBUG", false)
}
void logAccess(LogContext *pContext, struct timeval *tvStart, \
const char *format, ...)
{
char text[LINE_MAX];
int len;
va_list ap;
va_start(ap, format);
len = vsnprintf(text, sizeof(text), format, ap);
va_end(ap);
if (len >= sizeof(text))
{
len = sizeof(text) - 1;
}
doLogEx(pContext, tvStart, NULL, text, len, false, true);
}
const char *log_get_level_caption_ex(LogContext *pContext)
{
return get_log_level_caption(pContext->log_level);
}
#ifndef LOG_FORMAT_CHECK
void logEmerg(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_EMERG, "EMERG", true)
}
void logAlert(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_ALERT, "ALERT", true)
}
void logCrit(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_CRIT, "CRIT", true)
}
void logError(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_ERR, "ERROR", false)
}
void logWarning(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_WARNING, "WARNING", false)
}
void logNotice(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_NOTICE, "NOTICE", false)
}
void logInfo(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_INFO, "INFO", true)
}
void logDebug(const char *format, ...)
{
_DO_LOG((&g_log_context), LOG_DEBUG, "DEBUG", true)
}
#endif