【发布时间】:2012-08-05 00:35:58
【问题描述】:
我正在努力让我的一些通过 bash 重定向记录的应用程序的 logrotate 变得非常容易。基本上,我有一个将 STDIN 读入缓冲区的 C 程序。它读取这个缓冲区,每当遇到换行符时,它就会将收集到的输出写入文件。
这个程序的不同之处在于它不会让文件保持打开状态。 每次遇到新行时,它都会打开它以追加。这与 logrotate 实用程序一起非常好,但我想知道是否存在某种可怕的不可预见的问题,我没有考虑到稍后会遇到的问题。
在这个实用程序中实现信号处理并让 logrotate 发送一个 SIGHUP 会更好吗?我正在做的事情是否有可怕的性能损失?
所以通常你会在哪里做:
./app >> output.log
使用记录器实用程序:
./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;
}
谢谢!
【问题讨论】:
-
您是否考虑过只使用 copytruncate 进行 logrotate 而不是重新发明
tee? -
没有,因为我也没听说过。谢谢,我去看看!
-
有趣的是,
copytruncate正是我一直需要的。谢谢。 -
我们使用
logrotate与长时间运行的守护进程一起工作,我们只需配置logrotate以发出信号(SIGHUP或SIGUSR1)和日志记录进程仅关闭和重新打开文件当收到这样的信号时。我找不到在每个输出行上关闭和打开文件的任何理由,但是如果您仍然要缓冲自己,为什么要使用fopen()而不是open()? -
请记住,在截断之前复制日志可能需要一段时间,并且两次操作之间的所有日志都将丢失 - 如手册页中所述。