详解printf重定向到文件中,打印日志的实现

2023-05-16

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

详解printf重定向到文件中,打印日志的实现 的相关文章

  • printf什么时候打印失败?

    C 中的 printf 函数并不总是在屏幕上打印输出 例如 如果您忘记将 n 放在字符串末尾 则您正在 printfing 有时您不会得到 o p printf 不打印时是否还有其他一些情况 我记得有人说过这样的条件有7个 你们可以帮忙吗
  • 在 C/C++ 中打印前导空格和零

    我需要在数字前打印一些前导空格和零 以便输出如下所示 00015 22 00111 8 126 在这里 我需要打印leading spaces当数字是even and leading zero when odd 我是这样做的 int i d
  • printf("\033c") 是什么意思?

    我正在寻找一种在关闭程序后 重置 Unix 终端窗口的方法 并偶然发现printf 033c 效果很好 但我就是无法理解 我去了man console codes由于我对 Unix c 编程有点缺乏经验 所以它不是很有帮助 有人可以解释一下
  • 为什么当我们使用相同的源和目标时 sprintf 和 snprintf 的行为不同?

    我有一个使用 sprintf 的简单代码 include
  • 使用 system.out.printf 格式化 java 字符串

    我一直在寻找很多关于 java 中的 System out printf 格式化字符串输出的问题 但我似乎不明白如何使用它 我正在尝试打印看起来像这样的漂亮专栏 601 GoPro Hero5 Black 276 95 602 GoPro
  • Xcode 在 C++ 控制台中不显示任何内容

    我只是想将 Xcode 用于一个非常小的 C 项目 并且想看看 控制台中有一些打印 问题是我没有看到任何东西 我尝试运行一个非常简单的代码 include
  • 避免 printf() 中的尾随零

    我一直在发现 printf 系列函数的格式说明符 我想要的是能够打印小数点后最大给定位数的双精度 或浮点数 如果我使用 printf 1 3f 359 01335 printf 1 3f 359 00999 I get 359 013 35
  • printf 命令导致段错误? [复制]

    这个问题在这里已经有答案了 当我尝试初始化一个大型的二维字符数组时 它工作得很好 但是当我添加一个简单的打印命令时 它给了我一个分段错误 关于为什么会发生这种情况有什么想法吗 include
  • JavaScript 相当于 printf/String.Format

    我正在寻找一个与 C PHP 相当的 JavaScriptprintf 或者对于 C Java 程序员来说 String Format IFormatProvider对于 NET 我的基本要求是目前数字的千位分隔符格式 但处理大量组合 包括
  • printf 类型提升和符号扩展

    我对 printf 和一般情况下类型提升如何发生感到困惑 我尝试了以下代码 unsigned char uc 255 signed char sc 128 printf unsigned char value d n uc printf s
  • bash + for循环+输出索引号和元素

    这是我的数组 ARRAY one two three 如何打印数组以便得到如下输出 index i element i 使用printf or for我在下面使用的循环 1 one 2 two 3 three 一些笔记供我参考 打印数组的1
  • 从 Visual Studio 的哪个版本开始 vsnprintf 基本上符合标准?

    根据微软的文档vsnprintf https msdn microsoft com en us library 1kt27hek aspx 至少从 2003 版 Visual Studio 开始 该函数就是 C 运行时库的一部分 int v
  • 向 printf 传递太多参数

    任何已经工作了一周以上的 C 程序员都遇到过因调用而导致的崩溃printf格式说明符多于实际参数 例如 printf Gonna s and s s crash burn 然而 当你通过时 是否会发生类似的糟糕事情 too manyprin
  • 使用 sprintf 打印元素数量可变的向量

    在下面的代码中 我可以打印向量中的所有元素item用空格分隔为 item 123 456 789 sprintf d d d item ans 123 456 789 我怎样才能做到这一点而不必输入那么多 d作为元素的数量item 最简单的
  • 仅使用 fprintf 和 fscanf 替换文本文件中的字符串

    抱歉问这么简单的问题 这是我作业的一部分 我被困住了 如你看到的 include
  • %d 为长整型

    下面的代码正确吗 据我的理解 它应该不能正常工作 但是在Dev C 编译器上 它可以正常工作 有人可以详细解释一下吗 include
  • printf 字符串,可变长度项

    define SIZE 9 int number 5 char letters SIZE this wont be null terminated char fmt string 20 sprintf fmt string d ds SIZ
  • printf 似乎忽略了字符串精度

    所以 我有点难受 根据man 3 printf在我的系统上 字符串格式 5s 应使用指定的精度来限制从给定字符串参数打印的字符数 man 3 printf PRINTF 3 BSD Library Functions Manual PRIN
  • C 编程:正向变量参数列表

    我正在尝试编写一个函数 它接受可变数量的参数 如 printf 执行一些操作 然后将变量列表传递给 printf 我不知道如何做到这一点 因为它似乎必须将它们推入堆栈 大约是这样的 http pastie org 694844 http p
  • C++ 的 String.Format

    正在寻找 NET 的 String Format 等函数的 C 实现 显然有 printf 及其变体 但我正在寻找具有位置的东西 如下所示 String Format 您好 0 您是 1 岁 感觉如何 1 姓名 年龄 这是必要的 因为我们将

随机推荐