diff --git a/src/tlog.c b/src/tlog.c index 47b1cfe..66579df 100644 --- a/src/tlog.c +++ b/src/tlog.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,6 @@ #include #include #include -#include #include #ifndef likely @@ -40,6 +40,8 @@ #define TLOG_BUFF_LEN (PATH_MAX + TLOG_LOG_NAME_LEN * 3) #define TLOG_SUFFIX_GZ ".gz" #define TLOG_SUFFIX_LOG "" +#define TLOG_MAX_LINE_SIZE_SET (1024 * 8) +#define TLOG_MIN_LINE_SIZE_SET (128) #define TLOG_SEGMENT_MAGIC 0xFF446154 @@ -57,6 +59,9 @@ struct tlog_log { char logdir[PATH_MAX]; char logname[TLOG_LOG_NAME_LEN]; char suffix[TLOG_LOG_NAME_LEN]; + char pending_logfile[PATH_MAX]; + int rename_pending; + int fail; int logsize; int logcount; int block; @@ -66,12 +71,15 @@ struct tlog_log { int multi_log; int logscreen; int segment_log; - + unsigned int max_line_size; + tlog_output_func output_func; void *private_data; time_t last_try; time_t last_waitpid; + mode_t file_perm; + mode_t archive_perm; int waiters; int is_exit; @@ -97,13 +105,13 @@ struct tlog_segment_log_head { struct tlog_loginfo info; unsigned short len; char data[0]; -} __attribute__((packed)); +} __attribute__((packed)); struct tlog_segment_head { unsigned int magic; unsigned short len; char data[0]; -} __attribute__((packed)); +} __attribute__((packed)); struct oldest_log { char name[TLOG_LOG_NAME_LEN]; @@ -166,8 +174,8 @@ static int _tlog_mkdir(const char *path) if (access(path, F_OK) == 0) { return 0; } - - while(*path == ' ' && *path != '\0') { + + while (*path == ' ' && *path != '\0') { path++; } @@ -283,11 +291,37 @@ static int _tlog_gettime(struct tlog_time *cur_time) return 0; } +void tlog_set_maxline_size(struct tlog_log *log, int size) +{ + if (log == NULL) { + return; + } + + if (size < TLOG_MIN_LINE_SIZE_SET) { + size = TLOG_MIN_LINE_SIZE_SET; + } else if (size > TLOG_MAX_LINE_SIZE_SET) { + size = TLOG_MAX_LINE_SIZE_SET; + } + + log->max_line_size = size; +} + +void tlog_set_permission(struct tlog_log *log, unsigned int file, unsigned int archive) +{ + log->file_perm = file; + log->archive_perm = archive; +} + int tlog_localtime(struct tlog_time *tm) { return _tlog_gettime(tm); } +tlog_log *tlog_get_root() +{ + return tlog.root; +} + void tlog_set_private(tlog_log *log, void *private_data) { if (log == NULL) { @@ -311,19 +345,19 @@ static int _tlog_format(char *buff, int maxlen, struct tlog_loginfo *info, void int len = 0; int total_len = 0; struct tlog_time *tm = &info->time; - void* unused __attribute__ ((unused)); + void *unused __attribute__((unused)); unused = userptr; if (tlog.root->multi_log) { /* format prefix */ - len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5d][%4s][%17s:%-4d] ", - tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(), + len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5d][%4s][%17s:%-4d] ", + tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, getpid(), tlog_get_level_string(info->level), info->file, info->line); } else { /* format prefix */ - len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ", - tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, + len = snprintf(buff, maxlen, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d][%5s][%17s:%-4d] ", + tm->year, tm->mon, tm->mday, tm->hour, tm->min, tm->sec, tm->usec / 1000, tlog_get_level_string(info->level), info->file, info->line); } @@ -359,7 +393,7 @@ static int _tlog_root_log_buffer(char *buff, int maxlen, void *userptr, const ch } if (tlog.root->segment_log) { - log_head = (struct tlog_segment_log_head *) buff; + log_head = (struct tlog_segment_log_head *)buff; len += sizeof(*log_head); memcpy(&log_head->info, &info_inter->info, sizeof(log_head->info)); } @@ -400,7 +434,7 @@ static int _tlog_print_buffer(char *buff, int maxlen, void *userptr, const char { int len; int total_len = 0; - void* unused __attribute__ ((unused)); + void *unused __attribute__((unused)); unused = userptr; @@ -438,7 +472,7 @@ static int _tlog_need_drop(struct tlog_log *log) } /* if free buffer length is less than min line length */ - if (maxlen < TLOG_MAX_LINE_LEN) { + if (maxlen < log->max_line_size) { log->dropped++; ret = 0; } @@ -450,14 +484,14 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v { int len; int maxlen = 0; - char buff[TLOG_MAX_LINE_LEN]; - struct tlog_segment_head *segment_head = NULL; if (log == NULL || format == NULL) { return -1; } + char buff[log->max_line_size]; + if (log->buff == NULL) { return -1; } @@ -469,7 +503,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v len = print_callback(buff, sizeof(buff), userptr, format, ap); if (len <= 0) { return -1; - } else if (len >= TLOG_MAX_LINE_LEN) { + } else if (len >= log->max_line_size) { strncpy(buff, "[LOG TOO LONG, DISCARD]\n", sizeof(buff)); buff[sizeof(buff) - 1] = '\0'; len = strnlen(buff, sizeof(buff)); @@ -490,7 +524,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v } /* if free buffer length is less than min line length */ - if (maxlen < TLOG_MAX_LINE_LEN) { + if (maxlen < log->max_line_size) { if (log->end != log->start) { tlog.notify_log = log; pthread_cond_signal(&tlog.cond); @@ -502,7 +536,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v pthread_mutex_unlock(&tlog.lock); return -1; } - + pthread_mutex_unlock(&tlog.lock); pthread_mutex_lock(&log->lock); log->waiters++; @@ -516,7 +550,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v pthread_mutex_lock(&tlog.lock); } - } while (maxlen < TLOG_MAX_LINE_LEN); + } while (maxlen < log->max_line_size); if (log->segment_log) { segment_head = (struct tlog_segment_head *)(log->buff + log->end); @@ -532,7 +566,7 @@ static int _tlog_vprintf(struct tlog_log *log, vprint_callback print_callback, v } /* if remain buffer is not enough for a line, move end to start of buffer. */ - if (log->end > log->buffsize - TLOG_MAX_LINE_LEN) { + if (log->end > log->buffsize - log->max_line_size) { log->ext_end = log->end; log->end = 0; } @@ -562,12 +596,12 @@ int tlog_printf(struct tlog_log *log, const char *format, ...) return len; } -static int _tlog_early_print(const char *format, va_list ap) +static int _tlog_early_print(const char *format, va_list ap) { char log_buf[TLOG_MAX_LINE_LEN]; size_t len = 0; size_t out_len = 0; - int unused __attribute__ ((unused)); + int unused __attribute__((unused)); if (tlog_disable_early_print) { return 0; @@ -643,13 +677,13 @@ static int _tlog_rename_logfile(struct tlog_log *log, const char *log_file) return -1; } - snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%s", + snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d%s", log->logdir, log->logname, logtime.year, logtime.mon, logtime.mday, logtime.hour, logtime.min, logtime.sec, log->suffix); while (access(archive_file, F_OK) == 0) { i++; - snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d-%d%s", + snprintf(archive_file, sizeof(archive_file), "%s/%s-%.4d%.2d%.2d-%.2d%.2d%.2d-%d%s", log->logdir, log->logname, logtime.year, logtime.mon, logtime.mday, logtime.hour, logtime.min, logtime.sec, i, log->suffix); } @@ -658,6 +692,8 @@ static int _tlog_rename_logfile(struct tlog_log *log, const char *log_file) return -1; } + chmod(archive_file, log->archive_perm); + return 0; } @@ -666,7 +702,7 @@ static int _tlog_list_dir(const char *path, list_callback callback, void *userpt DIR *dir = NULL; struct dirent *ent; int ret = 0; - const char* unused __attribute__ ((unused)) = path; + const char *unused __attribute__((unused)) = path; dir = opendir(path); if (dir == NULL) { @@ -699,7 +735,7 @@ static int _tlog_count_log_callback(const char *path, struct dirent *entry, void struct count_log *count_log = (struct count_log *)userptr; struct tlog_log *log = count_log->log; char logname[TLOG_LOG_NAME_LEN * 2]; - const char* unused __attribute__ ((unused)) = path; + const char *unused __attribute__((unused)) = path; if (strstr(entry->d_name, log->suffix) == NULL) { return 0; @@ -1023,16 +1059,41 @@ static int _tlog_archive_log(struct tlog_log *log) } } +void _tlog_get_log_name_dir(struct tlog_log *log) +{ + char log_file[PATH_MAX]; + if (log->fd > 0) { + close(log->fd); + log->fd = -1; + } + + pthread_mutex_lock(&tlog.lock); + strncpy(log_file, log->pending_logfile, sizeof(log_file) - 1); + log_file[sizeof(log_file) - 1] = '\0'; + strncpy(log->logdir, dirname(log_file), sizeof(log->logdir)); + log->logdir[sizeof(log->logdir) - 1] = '\0'; + strncpy(log_file, log->pending_logfile, PATH_MAX); + log_file[sizeof(log_file) - 1] = '\0'; + strncpy(log->logname, basename(log_file), sizeof(log->logname)); + log->logname[sizeof(log->logname) - 1] = '\0'; + pthread_mutex_unlock(&tlog.lock); +} + static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen) { int len; - int unused __attribute__ ((unused)); + int unused __attribute__((unused)); - if (bufflen <= 0) { + if (bufflen <= 0 || log->fail) { return 0; } - /* output log to screen */ + if (log->rename_pending) { + _tlog_get_log_name_dir(log); + log->rename_pending = 0; + } + + /* output log to screen */ if (log->logscreen) { unused = write(STDOUT_FILENO, buff, bufflen); } @@ -1072,7 +1133,7 @@ static int _tlog_write(struct tlog_log *log, const char *buff, int bufflen) } snprintf(logfile, sizeof(logfile), "%s/%s", log->logdir, log->logname); log->filesize = 0; - log->fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0640); + log->fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, log->file_perm); if (log->fd < 0) { if (print_errmsg == 0) { return -1; @@ -1131,7 +1192,6 @@ static int _tlog_any_has_data_locked(void) return 0; } - static int _tlog_any_has_data(void) { int ret = 0; @@ -1162,7 +1222,7 @@ static int _tlog_wait_pids(void) continue; } - last_log = next; + last_log = next; next->last_waitpid = now; pthread_mutex_unlock(&tlog.lock); _tlog_wait_pid(next, 0); @@ -1263,7 +1323,6 @@ static void _tlog_wakeup_waiters(struct tlog_log *log) pthread_mutex_unlock(&log->lock); } - static void _tlog_write_one_segment_log(struct tlog_log *log, char *buff, int bufflen) { struct tlog_segment_head *segment_head = NULL; @@ -1345,10 +1404,10 @@ static void *_tlog_work(void *arg) int log_dropped = 0; struct tlog_log *log = NULL; struct tlog_log *loop_log = NULL; - void* unused __attribute__ ((unused)); + void *unused __attribute__((unused)); unused = arg; - + while (1) { log_len = 0; log_extlen = 0; @@ -1430,7 +1489,7 @@ static void *_tlog_work(void *arg) void tlog_set_early_printf(int enable) { - tlog_disable_early_print = (enable == 0) ? 1 : 0; + tlog_disable_early_print = (enable == 0) ? 1 : 0; } const char *tlog_get_level_string(tlog_level level) @@ -1518,10 +1577,14 @@ tlog_level tlog_getlevel(void) return tlog_set_level; } +void tlog_set_logfile(const char *logfile) +{ + tlog_rename_logfile(tlog.root, logfile); +} + tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int buffsize, unsigned int flag) { struct tlog_log *log = NULL; - char log_file[PATH_MAX]; if (tlog.run == 0) { fprintf(stderr, "tlog is not initialized."); @@ -1546,22 +1609,19 @@ tlog_log *tlog_open(const char *logfile, int maxlogsize, int maxlogcount, int bu log->filesize = 0; log->zip_pid = -1; log->is_exit = 0; + log->fail = 0; log->waiters = 0; log->block = ((flag & TLOG_NONBLOCK) == 0) ? 1 : 0; log->nocompress = ((flag & TLOG_NOCOMPRESS) == 0) ? 0 : 1; log->logscreen = ((flag & TLOG_SCREEN) == 0) ? 0 : 1; log->multi_log = ((flag & TLOG_MULTI_WRITE) == 0) ? 0 : 1; log->segment_log = ((flag & TLOG_SEGMENT) == 0) ? 0 : 1; + log->max_line_size = TLOG_MAX_LINE_LEN; log->output_func = _tlog_write; + log->file_perm = S_IRUSR | S_IWUSR | S_IRGRP; + log->archive_perm = S_IRUSR | S_IRGRP; - strncpy(log_file, logfile, sizeof(log_file) - 1); - log_file[sizeof(log_file) - 1] = '\0'; - strncpy(log->logdir, dirname(log_file), sizeof(log->logdir)); - log->logdir[sizeof(log->logdir) - 1] = '\0'; - strncpy(log_file, logfile, PATH_MAX); - log_file[sizeof(log_file) - 1] = '\0'; - strncpy(log->logname, basename(log_file), sizeof(log->logname)); - log->logname[sizeof(log->logname) - 1] = '\0'; + tlog_rename_logfile(log, logfile); if (log->nocompress) { strncpy(log->suffix, TLOG_SUFFIX_LOG, sizeof(log->suffix)); } else { @@ -1605,6 +1665,58 @@ void tlog_close(tlog_log *log) log->is_exit = 1; } +void tlog_rename_logfile(struct tlog_log *log, const char *logfile) +{ + pthread_mutex_lock(&tlog.lock); + strncpy(log->pending_logfile, logfile, sizeof(log->pending_logfile) - 1); + pthread_mutex_unlock(&tlog.lock); + log->rename_pending = 1; +} + +static void tlog_fork_prepare(void) +{ + if (tlog.root == NULL) { + return; + } + + pthread_mutex_lock(&tlog.lock); +} + +static void tlog_fork_parent(void) +{ + if (tlog.root == NULL) { + return; + } + + pthread_mutex_unlock(&tlog.lock); +} + +static void tlog_fork_child(void) +{ + pthread_attr_t attr; + tlog_log *next; + if (tlog.root == NULL) { + return; + } + + pthread_attr_init(&attr); + int ret = pthread_create(&tlog.tid, &attr, _tlog_work, NULL); + if (ret != 0) { + fprintf(stderr, "create tlog work thread failed, %s\n", strerror(errno)); + goto errout; + } + + goto out; +errout: + next = tlog.log; + while (next) { + next->fail = 1; + next = next->next; + } +out: + pthread_mutex_unlock(&tlog.lock); +} + int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize, unsigned int flag) { pthread_attr_t attr; @@ -1616,7 +1728,7 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize return -1; } - if (buffsize > 0 && buffsize < TLOG_MAX_LINE_LEN * 2) { + if (buffsize > 0 && buffsize < TLOG_MAX_LINE_SIZE_SET * 2) { fprintf(stderr, "buffer size is invalid.\n"); return -1; } @@ -1645,6 +1757,9 @@ int tlog_init(const char *logfile, int maxlogsize, int maxlogcount, int buffsize } tlog.root = log; + if (flag & TLOG_SUPPORT_FORK) { + pthread_atfork(&tlog_fork_prepare, &tlog_fork_parent, &tlog_fork_child); + } return 0; errout: if (tlog.tid > 0) { diff --git a/src/tlog.h b/src/tlog.h index 507f629..1e59a28 100644 --- a/src/tlog.h +++ b/src/tlog.h @@ -1,19 +1,20 @@ /* * tinylog - * Copyright (C) 2018-2020 Ruilin Peng (Nick) + * Copyright (C) 2018-2021 Ruilin Peng (Nick) * https://github.com/pymumu/tinylog */ #ifndef TLOG_H #define TLOG_H #include +#include #ifdef __cplusplus -#include +#include +#include #include #include -#include -#include +#include extern "C" { #endif /*__cplusplus */ @@ -60,6 +61,9 @@ struct tlog_time { /* enable log to screen */ #define TLOG_SCREEN (1 << 4) +/* enable suppport fork process */ +#define TLOG_SUPPORT_FORK (1 << 5) + struct tlog_loginfo { tlog_level level; const char *file; @@ -79,7 +83,7 @@ format: Log formats #define tlog(level, format, ...) tlog_ext(level, BASE_FILE_NAME, __LINE__, __func__, NULL, format, ##__VA_ARGS__) extern int tlog_ext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, ...) - __attribute__((format(printf, 6, 7))) __attribute__((nonnull (6))); + __attribute__((format(printf, 6, 7))) __attribute__((nonnull(6))); extern int tlog_vext(tlog_level level, const char *file, int line, const char *func, void *userptr, const char *format, va_list ap); /* write buff to log file */ @@ -91,6 +95,9 @@ extern int tlog_setlevel(tlog_level level); /* get log level */ extern tlog_level tlog_getlevel(void); +/* set log file */ +extern void tlog_set_logfile(const char *logfile); + /* enalbe log to screen */ extern void tlog_setlogscreen(int enable); @@ -132,6 +139,10 @@ extern int tlog_reg_log_output_func(tlog_log_output_func output, void *private_d struct tlog_log; typedef struct tlog_log tlog_log; + +/* get root log handler */ +extern tlog_log *tlog_get_root(void); + /* Function: open a new log stream, handler should close by tlog_close logfile: log file. @@ -149,12 +160,15 @@ extern int tlog_write(struct tlog_log *log, const char *buff, int bufflen); /* close log stream */ extern void tlog_close(tlog_log *log); +/* change log file */ +extern void tlog_rename_logfile(struct tlog_log *log, const char *logfile); + /* Function: Print log to log stream log: log stream format: Log formats */ -extern int tlog_printf(tlog_log *log, const char *format, ...) __attribute__((format(printf, 2, 3))) __attribute__((nonnull (1, 2))); +extern int tlog_printf(tlog_log *log, const char *format, ...) __attribute__((format(printf, 2, 3))) __attribute__((nonnull(1, 2))); /* Function: Print log to log stream with ap @@ -180,49 +194,78 @@ extern void *tlog_get_private(tlog_log *log); /* get local time */ extern int tlog_localtime(struct tlog_time *tm); +/* set max line size */ +extern void tlog_set_maxline_size(struct tlog_log *log, int size); + +/* +Function: set log file and archive permission +log: log stream +file: log file permission, default is 640 +archive: archive file permission, default is 440 +*/ + +extern void tlog_set_permission(struct tlog_log *log, mode_t file, mode_t archive); + #ifdef __cplusplus class Tlog { - using Stream = std::ostringstream; - using Buffer = std::unique_ptr>; public: - Tlog(){} - ~Tlog(){} - - static Tlog &Instance() { - static Tlog logger; - return logger; + Tlog(tlog_level level, const char *file, int line, const char *func, void *userptr) + { + level_ = level; + file_ = file; + line_ = line; + func_ = func; + userptr_ = userptr; } - Buffer LogStream(tlog_level level, const char *file, int line, const char *func, void *userptr) { - return Buffer(new Stream, [=](Stream *st) { - tlog_ext(level, file, line, func, userptr, "%s", st->str().c_str()); - delete st; - }); + ~Tlog() + { + tlog_ext(level_, file_, line_, func_, userptr_, "%s", msg_.str().c_str()); } + + std::ostream &Stream() + { + return msg_; + } + +private: + tlog_level level_; + const char *file_; + int line_; + const char *func_; + void *userptr_; + std::ostringstream msg_; }; class TlogOut { - using Stream = std::ostringstream; - using Buffer = std::unique_ptr>; public: - TlogOut(){} - ~TlogOut(){} - - static TlogOut &Instance() { - static TlogOut logger; - return logger; + TlogOut(tlog_log *log) + { + log_ = log; } - Buffer Out(tlog_log *log) { - return Buffer(new Stream, [=](Stream *st) { - tlog_printf(log, "%s", st->str().c_str()); - delete st; - }); + ~TlogOut() + { + if (log_ == nullptr) { + return; + } + + tlog_printf(log_, "%s", msg_.str().c_str()); } + + std::ostream &Stream() + { + return msg_; + } + +private: + tlog_log *log_; + std::ostringstream msg_; }; -#define Tlog_logger (Tlog::Instance()) -#define Tlog_stream(level) if (tlog_getlevel() <= level) *Tlog_logger.LogStream(level, BASE_FILE_NAME, __LINE__, __func__, NULL) +#define Tlog_stream(level) \ + if (tlog_getlevel() <= level) \ + Tlog(level, BASE_FILE_NAME, __LINE__, __func__, NULL).Stream() #define tlog_debug Tlog_stream(TLOG_DEBUG) #define tlog_info Tlog_stream(TLOG_INFO) #define tlog_notice Tlog_stream(TLOG_NOTICE) @@ -230,8 +273,7 @@ public: #define tlog_error Tlog_stream(TLOG_ERROR) #define tlog_fatal Tlog_stream(TLOG_FATAL) -#define Tlog_out_logger (TlogOut::Instance()) -#define tlog_out(stream) (*Tlog_out_logger.Out(stream)) +#define tlog_out(stream) TlogOut(stream).Stream() } /*__cplusplus */ #else @@ -241,5 +283,5 @@ public: #define tlog_warn(...) tlog(TLOG_WARN, ##__VA_ARGS__) #define tlog_error(...) tlog(TLOG_ERROR, ##__VA_ARGS__) #define tlog_fatal(...) tlog(TLOG_FATAL, ##__VA_ARGS__) -#endif +#endif #endif // !TLOG_H