printf是将信息打印到终端,但是有时当我们需要打印的信息比较多时,终端无法将所有信息都能够保留在屏幕上,这样我们就不能在终端获取我们想要的信息了,重定向很好的帮我们解决了这个问题,下面我就通过重定向printf将打印信息打印到文件中,这也相当于一个打印日志。
打印日志的功能是这样的,日志文件命名为YYYYMMDD.log,例如20180530.log,默认保存在与执行程序同一目录下;
若连续记录,则在每天0点重新生成当天日志;若程序运行时已有当天的日志,新的调试信息追加到该日志文件末。
创建一个线程,用来检测时间,当过了24点进入新的一天时,已追加的方式重新创建一个日志文件 ,这样就可以将打印信息都记录到日志文件中,当然前提是标准输出已经重定向到文件了,这才是本文的重点。
因为标准输入输出是行缓存,而我们的文件属于全缓存,所以我们需要将缓存区设置为无缓冲直接写到文件中,使用setbuf函数设置缓存区缓存为无缓存,使用dup2重定向标准输出为文件描述符
setvbuf(stdout, NULL, _IOLBF, 0) != 0;
dup2(file_fd, STDOUT_FILENO)
下面附上整个项目代码,该代码运行在多进程中
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "type_def.h"
static void *log_thread(void *arg);
static void setSysTime(char *timeStr);
static void child_process(void);
int main(int argc, const char *argv[])
{
pthread_t thread_log = -1;
UINT32 i = 0;
struct tm *p_time = NULL;
time_t time_s = 0;
pid_t pid = 0;
/* 设置系统时间 */
setSysTime("2018.5.30 23:59:50");
pid = fork();
if (pid < 0)
{
perror("fail to fork");
return -1;
}
else if (0 == pid)
{
child_process();
}
else
{
if (pthread_create(&thread_log, NULL, log_thread, NULL) != 0)
{
perror("fail to pthread_create");
return -1;
}
while (1)
{
sleep(1);
time(&time_s);
p_time = localtime(&time_s);
printf("parent :day = %d i = %d", p_time->tm_mday, i++);
}
}
return 0;
}
/** @fn child_process
* @brief 进程处理函数
*
*/
static void child_process(void)
{
UINT32 j = 0;
struct tm *p_time_c = NULL;
time_t time_s_c = 0;
pthread_t thread_log_child = -1;
if (pthread_create(&thread_log_child, NULL, log_thread, NULL) != 0)
{
perror("fail to pthread_create");
return;
}
while (1)
{
sleep(1);
time(&time_s_c);
p_time_c = localtime(&time_s_c);
printf("child :day = %d i = %d", p_time_c->tm_mday, j++);
}
return;
}
/** @fn log_thread
* @brief 线程处理函数,用来改变printf的重定向,每一天创建一个日志文件
*
* @param arg[] 没有使用
*
* @return 返回线程退出的状态值。可通过pthread_join函数获得
*/
static void *log_thread(void *arg)
{
struct tm *p_time = NULL;
time_t time_s = 0;
UINT32 yesterday = 0; // 用来保存当前的日期,与这一秒的日期比较,判断是否到新的一天
INT32 file_fd[2] = {-1}; // 日志文件的描述符数组
CHAR log_name[16] = {0}; // 日志文件名
UINT32 i = 0, j = 0;
#if 1
/* 设置缓存区为无缓存直接向流写入数据 */
fflush(stdout);
if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
{
perror("fail to setvbuf");
goto ERR_EXIT;
}
#endif
while (1)
{
time(&time_s);
p_time = localtime(&time_s);
if (NULL == p_time)
{
perror("fail to localtime");
goto ERR_EXIT;
}
/* 如果当前日期不等于yesterday,代表现在已经是新的一天,创建新的日志文件 */
if (yesterday != p_time->tm_mday)
{
snprintf(log_name, sizeof(log_name), "./%04d%02d%02d.log", p_time->tm_year + 1900,\
p_time->tm_mon + 1, p_time->tm_mday);
yesterday = p_time->tm_mday;
j = i;
i = (i + 1) % 2;
file_fd[i] = open(log_name, O_RDWR|O_APPEND|O_CREAT, 0664);
if (file_fd[i] < 0)
{
perror("fail to fopen");
goto ERR_EXIT;
}
if (-1 == dup2(file_fd[i], STDOUT_FILENO))
{
perror("fail to dup2");
goto ERR_EXIT;
}
close(file_fd[j]);
}
sleep(1);
}
ERR_EXIT:
if (file_fd[0] > 0)
{
close(file_fd[0]);
}
if (file_fd[1] > 0)
{
close(file_fd[1]);
}
pthread_exit(0);
}
/** @fn setSysTime
* @brief 用来设置系统的时间
*
* @param timeStr[IN] 指定设置时间
*
*/
static void setSysTime(char *timeStr)
{
struct timeval tv = {0};
struct tm localTime = {0};
UINT32 year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
sscanf(timeStr, "%d.%d.%d %d:%d:%d", &year, &month, &day, &hour, &minute, &second);
localTime.tm_sec = second;
localTime.tm_min = minute;
localTime.tm_hour = hour;
localTime.tm_mday = day;
localTime.tm_mon = month - 1;
localTime.tm_year = year - 1900;
/* 将struct tm结构体的时间转换成1970年1月1日以来逝去时间的秒数; */
tv.tv_sec = mktime(&localTime);
tv.tv_usec = 0;
settimeofday(&tv, NULL);
return;
}