通过C程序输出管道日志,以方便日志旋转

通过C程序输出管道日志,以方便日志旋转,c,bash,logrotate,C,Bash,Logrotate,我正试图让我的一些应用通过bash重定向进行日志旋转变得非常容易。基本上,我有一个C程序,可以将STDIN读入缓冲区。它读取这个缓冲区,每当遇到换行符时,它就会将收集到的输出写入一个文件 这个程序的不同之处在于它不会使文件保持打开状态。每次遇到新行时,它都会打开它进行追加。这在logrotate实用程序中非常有效,但我想知道是否有一些可怕的、不可预见的问题我没有考虑,我稍后会遇到 在这个实用程序中实现信号处理并让logrotate发送一个SIGHUP是否更好?我所做的事情是否会受到可怕的性能惩罚

我正试图让我的一些应用通过bash重定向进行日志旋转变得非常容易。基本上,我有一个C程序,可以将STDIN读入缓冲区。它读取这个缓冲区,每当遇到换行符时,它就会将收集到的输出写入一个文件

这个程序的不同之处在于它不会使文件保持打开状态。每次遇到新行时,它都会打开它进行追加。这在logrotate实用程序中非常有效,但我想知道是否有一些可怕的、不可预见的问题我没有考虑,我稍后会遇到

在这个实用程序中实现信号处理并让logrotate发送一个SIGHUP是否更好?我所做的事情是否会受到可怕的性能惩罚

所以通常情况下,你会:

./app >> output.log
使用记录器util,您可以执行以下操作:

./app | ./mylogger output.log
虽然我在C语言方面太差了,但我对它的最佳实践不是很精通。任何指导都将不胜感激

以下是消息来源:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define BUFSIZE 1024
#define MAX_WRITE_FAILS 3

/**
 * outputs the given content to the specified file.
 */
int file_output(char *filename, char *content, size_t content_length)
{
    FILE *fp;
    fp  =   fopen(filename, "a");
    content[content_length + 1] =   '\0';
    if(fp == NULL) return errno;
    fwrite(content, sizeof(char), content_length, fp);
    fclose(fp);
    return 0;
}

/**
 * Loops over STDIN and whenever it finds a newline, sends the current content
 * buffer to the file specified on the command line.
 */
int main(int argc, char *argv[])
{
    int i;
    char buffer[BUFSIZE];
    char *content           =   malloc(sizeof(char) * BUFSIZE);
    size_t content_size     =   0;
    int content_buf_size    =   BUFSIZE;
    int write_failures      =   0;
    char *file;

    if(argc < 2)
    {
        fprintf(stderr, "Usage: logger <file>");
        exit(1);
    }
    file    =   argv[1];

    // loop over STDIN
    while(fgets(buffer, BUFSIZE, stdin))
    {
        int output_err;
        int buflength   =   strlen(buffer);

        // loop over character for character, searching for newlines and
        // appending our buffer to the output content as we go along
        for(i = 0; i < buflength; i++)
        {
            char *old   =   content;

            // check if we have a newline or end of string
            if(buffer[i] == '\n' || buffer[i] == '\0' || (i != (buflength - 1) && buffer[i] == '\r' && buffer[i+1] == '\n'))
            {
                content[content_size]   =   '\n';
                output_err  =   file_output(file, content, content_size + 1);
                if(output_err == 0)
                {
                    // success! reset the content size (ie more or less resets
                    // the output content string)
                    content_size    =   0;
                    write_failures  =   0;
                }
                else
                {
                    // write failed, try to keep going. this will preserve our
                    // newline so that the next newline we encounter will write
                    // both lines (this AND and the next).
                    content_size++;
                    write_failures++;
                }
            }

            if(write_failures >= MAX_WRITE_FAILS)
            {
                fprintf(stderr, "Failed to write output to file %d times (errno: %d). Quitting.\n", write_failures, output_err);
                exit(3);
            }

            if(buffer[i] != '\n' && buffer[i] != '\r' && buffer[i] != '\0')
            {
                // copy buffer into content (if it's not a newline/null)
                content[content_size]   =   buffer[i];
                content_size++;
            }

            // check if we're pushing the limits of our content buffer
            if(content_size >= content_buf_size - 1)
            {
                // we need to up the size of our output buffer
                content_buf_size    +=  BUFSIZE;
                content =   (char *)realloc(content, sizeof(char) * content_buf_size);
                if(content == NULL)
                {
                    fprintf(stderr, "Failed to reallocate buffer memory.\n");
                    free(old);
                    exit(2);
                }
            }
        }
    }
    return 0;
}
#包括
#包括
#包括
#包括
#定义bufsize1024
#定义最大写入次数3
/**
*将给定内容输出到指定文件。
*/
int文件输出(字符*文件名、字符*内容、大小\u t内容\u长度)
{
文件*fp;
fp=fopen(文件名,“a”);
内容[内容长度+1]='\0';
if(fp==NULL)返回errno;
fwrite(内容,大小(字符),内容长度,fp);
fclose(fp);
返回0;
}
/**
*在STDIN上循环,只要找到换行符,就会发送当前内容
*缓冲区到命令行上指定的文件。
*/
int main(int argc,char*argv[])
{
int i;
字符缓冲区[BUFSIZE];
char*content=malloc(sizeof(char)*BUFSIZE);
大小内容大小=0;
int content\u buf\u size=BUFSIZE;
int write_failures=0;
字符*文件;
如果(argc<2)
{
fprintf(标准“用法:记录器”);
出口(1);
}
file=argv[1];
//标准数据环
while(fgets(缓冲区、BUFSIZE、stdin))
{
int输出错误;
int buflength=strlen(缓冲区);
//逐字符循环,搜索换行符和
//在进行时将缓冲区添加到输出内容
对于(i=0;i=最大写入失败)
{
fprintf(stderr,“无法将输出写入文件%d次(错误号:%d)。正在退出。\n”,写入失败,输出错误);
出口(3);
}
if(buffer[i]!='\n'&&buffer[i]!='\r'&&buffer[i]!='\0')
{
//将缓冲区复制到内容中(如果不是换行符/null)
内容[内容大小]=缓冲区[i];
内容大小++;
}
//检查我们是否超出了内容缓冲区的限制
如果(内容大小>=内容大小-1)
{
//我们需要增加输出缓冲区的大小
内容大小+=大小;
content=(char*)realloc(content,sizeof(char)*content\u buf\u size);
if(content==NULL)
{
fprintf(stderr,“未能重新分配缓冲区内存。\n”);
免费(旧);
出口(2);
}
}
}
}
返回0;
}

谢谢

由于我在评论中的建议是您所需要的,所以我添加它作为一个答案,并进行更多的解释

当您的日志应用程序无法被告知关闭其日志文件(通常通过SIGHUP)时,您可以在logrotate.conf中使用“copyruncate”选项

以下是手册页中的描述:


来源:

您是否考虑过对logrotate使用copyruncate而不是重新发明
tee
?没有,因为我也没有听说过。谢谢,我两个都去看看!有趣的是,
copyruncate
一直都在做我需要的事情。谢谢。我们在长时间运行的守护进程中使用
logrotate
,我们只需配置
logrotate
来发出信号(
SIGHUP
SIGUSR1
),并且只有在接收到此类信号时,日志记录过程才会关闭和重新打开文件。我找不到在每个输出行上关闭和打开文件的任何理由,但如果您仍要缓冲自己,为什么要使用
fopen()
而不是
open()
?请记住,在截断之前复制日志可能需要一段时间,并且两次操作之间的所有日志都将丢失-如手册页中所述。
  Truncate  the  original log file in place after creating a copy,
  instead of moving the old log file and optionally creating a new
  one,  It  can be used when some program can not be told to close
  its logfile and thus might continue writing (appending)  to  the
  previous log file forever.  Note that there is a very small time
  slice between copying the file and truncating it, so  some  log-
  ging  data  might be lost.  When this option is used, the create
  option will have no effect, as the old log file stays in  place.