瀏覽代碼

Update tlog

Nick Peng 4 年之前
父節點
當前提交
6338f1257c
共有 2 個文件被更改,包括 239 次插入82 次删除
  1. 162 47
      src/tlog.c
  2. 77 35
      src/tlog.h

+ 162 - 47
src/tlog.c

@@ -10,6 +10,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <libgen.h>
 #include <limits.h>
 #include <pthread.h>
 #include <stdarg.h>
@@ -21,7 +22,6 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <libgen.h>
 #include <unistd.h>
 
 #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) {

+ 77 - 35
src/tlog.h

@@ -1,19 +1,20 @@
 /*
  * tinylog
- * Copyright (C) 2018-2020 Ruilin Peng (Nick) <[email protected]>
+ * Copyright (C) 2018-2021 Ruilin Peng (Nick) <[email protected]>
  * https://github.com/pymumu/tinylog
  */
 
 #ifndef TLOG_H
 #define TLOG_H
 #include <stdarg.h>
+#include <sys/stat.h>
 
 #ifdef __cplusplus
-#include <string>
+#include <functional>
+#include <iostream>
 #include <memory>
 #include <sstream>
-#include <iostream>
-#include <functional>
+#include <string>
 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<Stream, std::function<void(Stream*)>>;
 public:
-    Tlog(){}
-    ~Tlog(){}
+    Tlog(tlog_level level, const char *file, int line, const char *func, void *userptr)
+    {
+        level_ = level;
+        file_ = file;
+        line_ = line;
+        func_ = func;
+        userptr_ = userptr;
+    }
 
-    static Tlog &Instance() {
-        static Tlog logger;
-        return logger;
+    ~Tlog()
+    {
+        tlog_ext(level_, file_, line_, func_, userptr_, "%s", msg_.str().c_str());
     }
 
-    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;
-        });
+    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<Stream, std::function<void(Stream*)>>;
 public:
-    TlogOut(){}
-    ~TlogOut(){}
+    TlogOut(tlog_log *log)
+    {
+        log_ = log;
+    }
+
+    ~TlogOut()
+    {
+        if (log_ == nullptr) {
+            return;
+        }
 
-    static TlogOut &Instance() {
-        static TlogOut logger;
-        return logger;
+        tlog_printf(log_, "%s", msg_.str().c_str());
     }
 
-    Buffer Out(tlog_log *log) {
-        return Buffer(new Stream, [=](Stream *st) {
-            tlog_printf(log, "%s", st->str().c_str());
-            delete st;
-        });
+    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