Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/ant/2.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 如何正确写入日志文件?_C_Linux_Logging - Fatal编程技术网

C 如何正确写入日志文件?

C 如何正确写入日志文件?,c,linux,logging,C,Linux,Logging,如何可靠地记录日志以确保多线程应用程序中条目的原子性?此外,我希望能够使用logrotate实用程序旋转日志 写入日志的最简单变体是next: 打开/重新打开日志文件 通过printf()写入条目 在退出时关闭日志文件 以下是我的例子: // default log level static Cl_loglevl loglevel = LOGLEVEL_NONE; // log file descriptor (open with Cl_openlog) static FILE *logfd =

如何可靠地记录日志以确保多线程应用程序中条目的原子性?此外,我希望能够使用logrotate实用程序旋转日志

写入日志的最简单变体是next:

  • 打开/重新打开日志文件
  • 通过
    printf()写入条目
  • 在退出时关闭日志文件
  • 以下是我的例子:

    // default log level
    static Cl_loglevl loglevel = LOGLEVEL_NONE;
    // log file descriptor (open with Cl_openlog)
    static FILE *logfd = NULL;
    
    /**
     * @brief Cl_openlog - open log file
     * @param logfile - file name
     * @return FILE struct or NULL if failed
     */
    FILE *Cl_openlog(const char *logfile, Cl_loglevl loglvl){
        if(logfd){
            Cl_putlog(LOGLEVEL_ERROR, "Reopen log file\n");
            fclose(logfd);
            logfd = NULL;
            char newname[PATH_MAX];
            snprintf(newname, PATH_MAX, "%s.old", logfile);
            if(rename(logfile, newname)) WARN("Can't rename old log file");
        }
        if(loglvl < LOGLEVEL_CNT) loglevel = loglvl;
        if(!logfile) return NULL;
        if(!(logfd = fopen(logfile, "w"))) WARN(_("Can't open log file"));
        return logfd;
    }
    
    /**
     * @brief Cl_putlog - put message to log file
     * @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
     * @param fmt - format and the rest part of message
     * @return amount of symbols saved in file
     */
    int Cl_putlog(Cl_loglevl lvl, const char *fmt, ...){
        if(lvl > loglevel || !logfd) return 0;
        char strtm[128];
        time_t t = time(NULL);
        struct tm *curtm = localtime(&t);
        strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm);
        int i = fprintf(logfd, "%s\t", strtm);
        va_list ar;
        va_start(ar, fmt);
        i += vfprintf(logfd, fmt, ar);
        va_end(ar);
        fflush(logfd);
        return i;
    }
    
    //默认日志级别
    静态Cl_loglevl loglevel=loglevel_NONE;
    //日志文件描述符(使用Cl_openlog打开)
    静态文件*logfd=NULL;
    /**
    *@brief Cl_openlog-打开日志文件
    *@param logfile-文件名
    *@return FILE struct或NULL(如果失败)
    */
    文件*Cl_openlog(const char*logfile,Cl_loglevl loglvl){
    如果(logfd){
    Cl_putlog(日志级别_错误,“重新打开日志文件”\n);
    fclose(logfd);
    logfd=NULL;
    char newname[PATH_MAX];
    snprintf(新名称,路径_MAX,“%s.old”,日志文件);
    if(rename(logfile,newname))警告(“无法重命名旧日志文件”);
    }
    如果(loglvl日志级别,消息将不会打印)
    *@param fmt-格式和消息的其余部分
    *@返回文件中保存的符号数量
    */
    内部日志(日志级别,常量字符*fmt,…){
    如果(lvl>loglevel | | |!logfd)返回0;
    char-strtm[128];
    时间t=时间(空);
    struct tm*curtm=本地时间(&t);
    strftime(strtm,128,“%Y/%m/%d-%H:%m”,curtm);
    int i=fprintf(logfd,“%s\t”,strtm);
    va_列表ar;
    va_启动(ar、fmt);
    i+=vfprintf(logfd、fmt、ar);
    va_端(ar);
    fflush(logfd);
    返回i;
    }
    
    调用
    Cl\u openlog
    允许将日志旋转一次。我可以在
    SIG_USR1
    处理程序中调用此函数,并通过logrotate发送此信号。但如何正确地写入文件以实现记录的原子性仍然不清楚


    对于这样简单的问题,我不想使用log4c这样的外部库。

    您可以确保所有日志记录都是通过单个线程完成的,或者使用互斥体保护日志记录功能。
    对于第一种情况,可以有一个工作线程在管道的读取端进行轮询;然后,每个线程将使用管道的写入端唤醒工作线程,并让它管理其日志(在堆的某个位置分配,并通过管道按地址传递)。
    对于SIGUSR1处理程序(logrotate),也可以这样做

    请注意,向管道()写入小于PIPE_BUF的数据保证不会被交错(因此它是原子的)。
    因此,仅写入堆存储地址始终是原子的


    最后但并非最不重要的一点是,您可以为每个线程使用不同的日志文件。

    嗯,我终于做到了:

    // array with all opened logs - for error/warning messages
    static Cl_log *errlogs = NULL;
    static int errlogsnum = 0;
    
    /**
     * @brief Cl_createlog - create log file: init mutex, test file open ability
     * @param log - log structure
     * @return 0 if all OK
     */
    int Cl_createlog(Cl_log *log){
        if(!log || !log->logpath || log->loglevel <= LOGLEVEL_NONE || log->loglevel >= LOGLEVEL_CNT) return 1;
        FILE *logfd = fopen(log->logpath, "a");
        if(!logfd){
            WARN("Can't open log file");
            return 2;
        }
        fclose(logfd);
        pthread_mutex_init(&log->mutex, NULL);
        errlogs = realloc(errlogs, (++errlogsnum) *sizeof(Cl_log));
        if(!errlogs) errlogsnum = 0;
        else memcpy(&errlogs[errlogsnum-1], log, sizeof(Cl_log));
        return 0;
    }
    
    /**
     * @brief Cl_putlog - put message to log file with/without timestamp
     * @param timest - ==1 to put timestamp
     * @param log - pointer to log structure
     * @param lvl - message loglevel (if lvl > loglevel, message won't be printed)
     * @param fmt - format and the rest part of message
     * @return amount of symbols saved in file
     */
    int Cl_putlogt(int timest, Cl_log *log, Cl_loglevl lvl, const char *fmt, ...){
        if(!log || !log->logpath || log->loglevel < 0 || log->loglevel >= LOGLEVEL_CNT) return 0;
        if(lvl > log->loglevel) return 0;
        if(pthread_mutex_lock(&log->mutex)) return 0;
        int i = 0;
        FILE *logfd = fopen(log->logpath, "a");
        if(!logfd) goto rtn;
        if(timest){
            char strtm[128];
            time_t t = time(NULL);
            struct tm *curtm = localtime(&t);
            strftime(strtm, 128, "%Y/%m/%d-%H:%M", curtm);
            i = fprintf(logfd, "%s\t", strtm);
        }
        va_list ar;
        va_start(ar, fmt);
        i += vfprintf(logfd, fmt, ar);
        va_end(ar);
        fclose(logfd);
    rtn:
        pthread_mutex_unlock(&log->mutex);
        return i;
    }
    
    //包含所有打开日志的数组-用于错误/警告消息
    静态Cl_log*errlogs=NULL;
    静态int errlogsnum=0;
    /**
    *@brief Cl_createlog-创建日志文件:初始化互斥,测试文件打开能力
    *@param log-日志结构
    *@如果一切正常,返回0
    */
    int Cl_createlog(Cl_log*log){
    如果(!log | |!log->logpath | | log->loglevel loglevel>=loglevel_CNT)返回1;
    文件*logfd=fopen(log->logpath,“a”);
    如果(!logfd){
    警告(“无法打开日志文件”);
    返回2;
    }
    fclose(logfd);
    pthread_mutex_init(&log->mutex,NULL);
    errlogs=realloc(errlogs,(++errlogsnum)*sizeof(Cl_log));
    如果(!errlogs)errlogsnum=0;
    else memcpy(&errlogs[errlogsnum-1],log,sizeof(Cl_log));
    返回0;
    }
    /**
    *@brief Cl_putlog-将消息放入带有/不带时间戳的日志文件
    *@param timest-==1以放置时间戳
    *@param log-指向日志结构的指针
    *@param lvl-消息日志级别(如果lvl>日志级别,消息将不会打印)
    *@param fmt-格式和消息的其余部分
    *@返回文件中保存的符号数量
    */
    int CLU putlogt(int timest、CLU log*log、CLU loglevl lvl、const char*fmt等){
    如果(!log | |!log->logpath | | log->loglevel<0 | | log->loglevel>=loglevel_CNT)返回0;
    如果(lvl>log->loglevel)返回0;
    if(pthread\u mutex\u lock(&log->mutex))返回0;
    int i=0;
    文件*logfd=fopen(log->logpath,“a”);
    如果(!logfd)转到rtn;
    if(timest){
    char-strtm[128];
    时间t=时间(空);
    struct tm*curtm=本地时间(&t);
    strftime(strtm,128,“%Y/%m/%d-%H:%m”,curtm);
    i=fprintf(logfd,“%s\t”,strtm);
    }
    va_列表ar;
    va_启动(ar、fmt);
    i+=vfprintf(logfd、fmt、ar);
    va_端(ar);
    fclose(logfd);
    rtn:
    pthread_mutex_unlock(&log->mutex);
    返回i;
    }
    
    Cl_createlog()
    中,我只是测试了打开给定文件和初始化互斥的能力。在
    Cl\u putlogt()
    中,我打开给定的文件,写入并关闭它。用于使任何线程的写入原子化的互斥体


    数组
    errlogs
    包含用于写入关键消息的所有打开的日志。

    无论何时访问日志文件(包括旋转时),都可以使用互斥锁。或者您应该让每个日志文件写入事件打开并关闭日志文件(仍然使用互斥锁以防止同时访问),但是如果您做了大量日志记录,也可能会影响性能。无论哪种方式,您都应该只在关键任务之后登录每个线程,这样日志互斥不会降低您的实际功能。此外,您还希望签出现有库(例如)。