Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 与logrotate兼容的日志记录_C_Linux_Logrotate - Fatal编程技术网

C 与logrotate兼容的日志记录

C 与logrotate兼容的日志记录,c,linux,logrotate,C,Linux,Logrotate,我正在编写一个Linux守护进程来编写日志。我想把原木按原木旋转。程序是用C语言编写的 通常,我的程序会在启动时打开日志文件,然后根据需要写入条目,最后在退出时关闭日志文件 为了使用logrotate支持日志旋转,我需要做哪些不同的事情?据我所知,每次logrotate完成工作时,我的程序都应该能够重新打开日志文件。然而,我在谷歌上搜索的源代码并没有明确说明重新打开日志文件的确切含义。我需要对旧文件做些什么吗?我可以创建另一个同名文件吗?我更喜欢非常具体的说明,比如一些简单的示例代码 我也明白应

我正在编写一个Linux守护进程来编写日志。我想把原木按原木旋转。程序是用C语言编写的

通常,我的程序会在启动时打开日志文件,然后根据需要写入条目,最后在退出时关闭日志文件

为了使用logrotate支持日志旋转,我需要做哪些不同的事情?据我所知,每次logrotate完成工作时,我的程序都应该能够重新打开日志文件。然而,我在谷歌上搜索的源代码并没有明确说明重新打开日志文件的确切含义。我需要对旧文件做些什么吗?我可以创建另一个同名文件吗?我更喜欢非常具体的说明,比如一些简单的示例代码

我也明白应该有一种方法告诉我的程序什么时候可以重新打开。我的程序已经有了一个D-Bus接口,我想用它来做这些通知

注意:我不需要关于如何配置logrotate的说明。这个问题只是关于如何使我自己的软件与之兼容

通常,我的程序会在启动时打开日志文件,然后 根据需要写入条目,然后在退出时关闭日志文件

为了支持日志轮换,我需要做哪些不同的事情 使用logrotate

不,您的程序应该像不了解logrotate一样工作

我需要对旧文件做些什么吗?我可以创建另一个同名文件吗


不可以。应该只打开和写入一个日志文件。Logrotate将检查该文件,如果文件太大,它会复制/保存旧部件,并截断当前日志文件。因此,您的程序应该是完全透明的—它不需要了解任何有关logrotate的信息。

有几种常见的方法:

  • 使用
    logrotate
    时,程序应该能够捕获一个信号(通常是SIGHUP)作为关闭和重新打开其日志文件的请求。然后
    logrotate
    在postrotate脚本中发送信号
  • 您使用的是
    logrotate
    ,而您的程序并不知道它,但可以重新启动。然后
    logrotate
    在postrotate脚本中重新启动程序。缺点:如果程序的启动成本很高,这可能是次优的
  • 您使用的是
    logrotate
    ,而您的程序并不知道它,但是您将copyruncate选项传递给
    logrotate
    。然后
    logrotate
    复制文件,然后将其截断。缺点:在比赛条件下,你可能会丢失信息。从
    rotatelog.conf
    manpage

    。。。请注意,复制文件和截断文件之间的时间间隔很短,因此一些日志数据可能会丢失

  • 您使用的是用于httpd Apache的实用程序
    rotatelogs
    。与直接写入文件不同,您可以通过编程将其日志传输到
    rotatelogs
    。然后
    rotatelogs
    管理不同的日志文件。缺点:您的程序应该能够登录到管道,否则您将需要安装一个命名的fifo


  • 但是要注意,对于关键日志,在每条消息之后关闭文件可能会很有趣,因为这样可以确保在应用程序崩溃的情况下,所有内容都已到达磁盘。

    虽然
    man logrotate
    示例使用HUP信号,但我建议使用
    USR1
    USR2
    ,因为使用HUP是常见的例如,在logrotate配置文件中

    /var/log/yourapp/log {
        rotate 7
        weekly
        postrotate
            /usr/bin/killall -USR1 yourapp
        endscript
    }
    

    一个棘手的问题是处理信号到达日志中间的情况。事实上,没有锁定原语(除了<代码> SEMIN POST)<代码>,这是一个有趣的问题。

    最简单的方法是使用一个专用线程等待,所有线程中的信号都被阻塞。在退出时,进程本身发送信号,并加入专用线程。例如

    #define  ROTATE_SIGNAL  SIGUSR1
    
    static pthread_t        log_thread;
    static pthread_mutex_t  log_lock = PTHREAD_MUTEX_INITIALIZER;
    static char            *log_path = NULL;
    static FILE *volatile   log_file = NULL;
    
    int log(const char *format, ...)
    {
        va_list  args;
        int      retval;
    
        if (!format)
            return -1;
        if (!*format)
            return 0;
    
        va_start(args, format);
        pthread_mutex_lock(&log_lock);
        if (!log_file)
            return -1;
        retval = vfprintf(log_file, format, args);
        pthread_mutex_unlock(&log_lock);
        va_end(args);
    
        return retval;
    }
    
    void *log_sighandler(void *unused)
    {
        siginfo_t info;
        sigset_t  sigs;
        int       signum;
    
        sigemptyset(&sigs);
        sigaddset(&sigs, ROTATE_SIGNAL);
    
        while (1) {
    
            signum = sigwaitinfo(&sigs, &info);
            if (signum != ROTATE_SIGNAL)
                continue;
    
            /* Sent by this process itself, for exiting? */
            if (info.si_pid == getpid())
                break;
    
            pthread_mutex_lock(&log_lock);
            if (log_file) {
                fflush(log_file);
                fclose(log_file);
                log_file = NULL;
            }
            if (log_path) {
                log_file = fopen(log_path, "a");
            }
            pthread_mutex_unlock(&log_lock);
        }
    
        /* Close time. */
        pthread_mutex_lock(&log_lock);
        if (log_file) {
            fflush(log_file);
            fclose(log_file);
            log_file = NULL;
        }
        pthread_mutex_unlock(&log_lock);
    
        return NULL;
    }
    
    /* Initialize logging to the specified path.
       Returns 0 if successful, errno otherwise. */
    int log_init(const char *path)
    {
        sigset_t          sigs;
        pthread_attr_t    attrs;
        int               retval;
    
        /* Block the rotate signal in all threads. */
        sigemptyset(&sigs);
        sigaddset(&sigs, ROTATE_SIGNAL);
        pthread_sigmask(SIG_BLOCK, &sigs, NULL);
    
        /* Open the log file. Since this is in the main thread,
           before the rotate signal thread, no need to use log_lock. */
        if (log_file) {
            /* You're using this wrong. */
            fflush(log_file);
            fclose(log_file);
        }
        log_file = fopen(path, "a");
        if (!log_file)
            return errno;
    
        log_path = strdup(path);
    
        /* Create a thread to handle the rotate signal, with a tiny stack. */
        pthread_attr_init(&attrs);
        pthread_attr_setstacksize(65536);
        retval = pthread_create(&log_thread, &attrs, log_sighandler, NULL);
        pthread_attr_destroy(&attrs);
        if (retval)
            return errno = retval;
    
        return 0;       
    }
    
    void log_done(void)
    {
        pthread_kill(log_thread, ROTATE_SIGNAL);
        pthread_join(log_thread, NULL);
        free(log_path);
        log_path = NULL;
    }
    
    其思想是在
    main()
    中,在记录或创建任何其他线程之前,调用
    log\u init(日志文件路径)
    ,注意日志文件路径的副本已保存。它设置信号掩码(由您可能创建的任何线程继承),并创建辅助线程。退出之前,调用
    log\u done()
    。要将某些内容记录到日志文件中,请像使用
    printf()
    一样使用
    log()

    我个人也会在
    vfprintf()
    行之前自动添加一个时间戳:

        struct timespec  ts;
        struct tm        tm;
    
        if (clock_gettime(CLOCK_REALTIME, &ts) == 0 &&
            localtime_r(&(ts.tv_sec), &tm) == &tm)
            fprintf(log_file, "%04d-%02d-%02d %02d:%02d:%02d.%03ld: ",
                              tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                              tm.tm_hour, tm.tm_min, tm.tm_sec,
                              ts.tv_nsec / 1000000L);
    

    这种
    YYYY-MM-DD HH:MM:SS.sss
    格式有一个很好的优点,即它接近全球标准(),并按正确的顺序排序。

    请注意,这需要“copyruncate”“logrotate配置-是在无法告知日志程序重新打开其日志文件时使用的回退解决方案。程序必须以附加模式打开日志文件才能工作,并且可能会丢失日志消息。更好的方法是让logrotate与程序交互(例如,向其发送unix信号,或使用其他程序细节),以便在程序旋转时关闭并重新打开日志文件。