詳細printfファイルへのリダイレクト、印刷ログの実装

4450 ワード

printfは端末に情報を印刷しますが、印刷する必要がある情報が多い場合、端末はすべての情報を画面に残すことができません.そうすれば、端末で私たちが望んでいる情報を取得することができません.リダイレクトはこの問題を解決するのに役立ちます.次に、printfにリダイレクトして印刷情報をファイルに印刷します.これも印刷ログに相当します.
ログを印刷する機能は、YYYYYMMDDという名前のログファイルである.log、例えば20180530.log、デフォルトは実行プログラムと同じディレクトリに保存されます.
連続して記録すると、毎日0時に当日ログが再生成される.プログラムの実行時に当日のログがある場合は、新しいデバッグ情報をログファイルの最後に追加します.
時間を検出するためのスレッドを作成し、24時を過ぎて新しい日に入ると、追加的にログファイルを再作成します.これにより、印刷情報をログファイルに記録することができます.もちろん、標準出力がファイルにリダイレクトされていることが前提です.これが本稿のポイントです.
標準入出力は行キャッシュであり、私たちのファイルは全キャッシュに属するため、キャッシュ領域をバッファなしに設定して直接ファイルに書き込む必要があります.setbuf関数を使用してキャッシュ領域をキャッシュなしに設定し、dup 2を使用して標準出力をファイル記述子にリダイレクトします.
setvbuf(stdout, NULL, _IOLBF, 0) != 0;
dup2(file_fd, STDOUT_FILENO)

マルチプロセスで実行されるプロジェクトコード全体を添付します.
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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;
}