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;
}