`
diecui1202
  • 浏览: 97924 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

多进程log4j日志丢失问题分析

    博客分类:
  • Java
阅读更多

一、背景:后台有很多任务,每个任务都是一个main函数(JVM或进程),但是所有的任务都加载同一个log4j.xml文件,即往同一份文件中输出日志。

二、原因追踪

在 log4j 的 DailyRollingFileAppender 类中:

void rollOver() throws IOException {

    /* Compute filename, but only if datePattern is specified */
    if (datePattern == null) {
      errorHandler.error("Missing DatePattern option in rollOver().");
      return;
    }

    String datedFilename = fileName+sdf.format(now);
    // It is too early to roll over because we are still within the
    // bounds of the current interval. Rollover will occur once the
    // next interval is reached.
    if (scheduledFilename.equals(datedFilename)) {
      return;
    }

    // close current file, and rename it to datedFilename
    this.closeFile();

    File target  = new File(scheduledFilename);
    if (target.exists()) {
      target.delete();
    }

    File file = new File(fileName);
    boolean result = file.renameTo(target);
    if(result) {
      LogLog.debug(fileName +" -> "+ scheduledFilename);
    } else {
      LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
    }

    try {
      // This will also close the file. This is OK since multiple
      // close operations are safe.
      this.setFile(fileName, false, this.bufferedIO, this.bufferSize);
    }
    catch(IOException e) {
      errorHandler.error("setFile("+fileName+", false) call failed.");
    }
    scheduledFilename = datedFilename;
}
该方法的作用是:在滚动备份时间间隔到的时刻,将前一时间间隔的日志备份,同时以非追加模式将新日志打到新日志文件中;

中间部分代码意思是:如果备份文件不存在,则备份,同时创建新日志文件;如果存在,则先删除掉,再备份;

 

好,问题在这个时刻就出现了:(假设A进程先进行滚动备份)

1、对于A进程:
a. 先将project.log备份(renameTo())为project.log.2009.08.18,然后创建project.log文件,并将日志写在project.log中;
b. 此时A进程持有project.log的文件句柄;而B进程仍然持有project.log.2009.08.18的文件句柄(尽管被重命名,但句柄不变);

2、对于B进程:发现以project.log.2009.08.18为文件名的文件已经存在,则将其删除(前一时间段的所有日志全没了),并将以project.log为文件名的文件重命名为project.log.2009.08.18,然后创建project.log文件;

3、此时A进程持有project.log.2009.08.18的文件句柄(被B进程重命名过的),而B进程持有最新创建的project.log

4、结果导致:前一时间段日志丢失,A、B进程在不同的文件里打日志;

三、解决方案

1、改变 rollOver() 方法的实现方式:定义 TaskDailyRollingFileAppender 类,该类继承至 FileAppender ,它与 DailyRollingFileAppender 的主要区别在于以下方法:

void rollOver() throws IOException {

    /* Compute filename, but only if datePattern is specified */
    if (datePattern == null) {
      errorHandler.error("Missing DatePattern option in rollOver().");
      return;
    }

    String datedFilename = fileName+sdf.format(now);
    // It is too early to roll over because we are still within the
    // bounds of the current interval. Rollover will occur once the
    // next interval is reached.
    if (scheduledFilename.equals(datedFilename)) {
      return;
    }

    // close current file, and rename it to datedFilename
    this.closeFile();

    File target  = new File(scheduledFilename);
    if (!target.exists()) {
        File file = new File(fileName);
        boolean result = file.renameTo(target);
        if (result) {
            LogLog.debug(fileName + " -> " + scheduledFilename);
        } else {
            LogLog.error("Failed to rename [" + fileName + "] to [" + scheduledFilename + "].");
        }
    }

    try {
        // This will also close the file. This is OK since multiple
        // close operations are safe.
        this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
    }
    catch(IOException e) {
      errorHandler.error("setFile("+fileName+", false) call failed.");
    }
    scheduledFilename = datedFilename;
}
改进后的 rollOver() 方法主要作用是:A进程先将日志重命名,然后创建新日志文件,B进程发现已经存在,则直接以追加模式切换到新的日志文件上去;

 

2、如果是任务,根据启动参数taskName 属性区分日志文件:

a. 目前所有后台任务在启动脚本里都加上了 -DtaskName 属性;
b. 定义 TaskDailyRollingFileAppender 类,该类继承 DailyRollingFileAppender,并覆盖其 setFile(String file) 方法:

public void setFile(String file) {
    String taskName = System.getProperty("taskName");
    if (!StringUtil.isEmpty(taskName)) {
        file = file + "." + taskName;
    }

    super.setFile(file);
}
 c. 这样实现后,对于不同的任务,日志文件名会以.taskName结尾,对于没有指定 taskName 的任务,不受影响;
0
0
分享到:
评论
3 楼 love398146779 2013-07-09  
分析的不错,收了。
2 楼 diecui1202 2011-05-25  
weilJava 写道
昨天被问到了多进程读写文件的问题
一点思路都没

这个问题确实比较头疼啊~能否借助文件来实现同步?
1 楼 weilJava 2011-05-25  
昨天被问到了多进程读写文件的问题
一点思路都没

相关推荐

    log4cplus封装

    - **log4cplus.appender.ALogger.UseLockFile**:布尔值,表示是否使用锁定文件以防止多个进程同时写入日志文件。 - **log4cplus.appender.ALogger.LockFile**:当UseLockFile为true时,指定锁文件的路径。 - **log4...

    Go-log一个基于Golang的日志模块

    考虑到Golang的多线程环境,Go-log很可能实现了并发安全的机制,确保在高并发情况下日志记录不会出现数据混乱或丢失。 5. **日志切割与归档**: 为了管理大量日志,Go-log可能具有日志文件自动切割和归档的功能,...

    Node.js 日志处理模块log4js

    log4js的高级用法中,还包括了日志的过滤、日志的格式化以及多进程下的日志处理等高级特性。这些特性能够帮助我们在生产环境中更好地管理和监控日志,快速响应和处理日志中记录的各种事件和问题。 总之,log4js作为...

    Django多进程滚动日志问题解决方案

    这篇文章主要介绍了Django多进程滚动日志问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用RotatingFileHandler控制日志文件的大小 # ...

    Linux C 实现日志打印功能

    4. **同步与异步**:日志打印可能涉及多线程环境,同步日志确保在同一时刻只有一个线程写入,可以使用互斥锁`mutex`来实现。而异步日志则将日志放入队列,由单独的线程处理,提高性能但可能会丢失部分日志。 5. **...

    C++Stream风格异步日志(muduo日志)

    在IT行业中,日志系统是不可或缺的一部分,它用于记录程序运行过程中的各种信息,帮助开发者在出现问题时进行调试和分析。C++Stream风格的异步日志,如"Muduo"日志,是一种高效且实用的日志解决方案。Muduo是由知名...

    通过zlog库进行了裁剪的log日志

    在IT行业中,日志记录是系统调试、性能分析和故障排查的重要工具。zlog是一个高效、易用且可裁剪的日志库,尤其适用于嵌入式设备和高性能服务器环境。本篇文章将深入探讨如何利用zlog库进行日志管理,以及如何根据...

    python+pytest接口自动化(15)-日志管理模块loguru简介.doc

    * 多进程多线程安全:Loguru 可以在多进程多线程环境中安全地记录日志,避免日志丢失或记录错乱。 使用 Loguru 模块需要先安装,安装命令为 `pip install loguru`。 简单示例: ``` from loguru import logger ...

    Log输出到sdcard

    在Android开发中,日志(Log)是调试和问题排查的重要工具。`Logcat`是Android系统提供的一个命令行工具,用于收集运行时的日志信息。这篇内容将深入讲解如何将`Log`输出到SD卡,以便于日后按日期查看和分析。 一、...

    多线程的日志记录模块

    由于多个线程可能同时尝试写入日志,如果没有适当的同步机制,可能会导致数据冲突和日志丢失。常见的解决策略包括使用互斥量(Mutex)、信号量(Semaphore)或者临界区(Critical Section)来确保同一时间只有一个...

    log4js-extras:Log4js 扩展

    Log4js是基于Node.js的日志框架,其设计灵感来源于Java的log4j。它允许开发者通过简单的API来记录不同级别的日志,如DEBUG、INFO、WARN、ERROR等,并且可以将日志输出到控制台、文件、网络甚至数据库。而log4js-...

    python中日志logging模块的性能及多进程详解

    由于Python的`logging`模块本身是线程安全的,但在多进程环境下,多个进程同时向同一个日志文件写入可能会导致数据混乱或丢失。 针对这个问题,一种常见的解决方案是为每个进程分配独立的日志文件。具体做法如下: ...

    基于socket分布式日志系统的设计与实现

    1. **日志采集**:在每个产生日志的节点上,我们需要一个轻量级的日志代理(如Log4j、Logback等),它负责收集本地的日志事件,并通过Socket发送到中央日志服务器。 2. **Socket通信**:日志代理和日志服务器之间的...

    PyPI 官网下载 | ConcurrentLogHandler-0.8.7.tar.gz

    在默认的`logging`模块中,日志处理器是线程不安全的,这可能导致在高并发环境下出现日志丢失或交错的问题。`ConcurrentLogHandler`通过使用锁和其他同步机制解决了这个问题,确保了在多线程或多进程环境下的日志...

    日志文件类

    这意味着多个线程同时写入日志时,不能出现数据冲突或丢失。因此,日志文件类需要采用互斥锁、信号量等同步机制来确保这一点。 在提供的`LogFile.cpp`和`LogFile.h`文件中,我们可以预见到这些功能的实现。`LogFile...

    oracle日志文件大全

    - 数据保护:归档日志文件的备份策略有助于防止数据丢失。 - **管理**: - 归档策略设置:包括何时进行归档、归档位置等。 - 归档日志的清理:根据归档日志的备份策略,定期清理过期的归档日志。 ##### 3. Alert...

    oracle重做日志教程

    - Oracle数据库通常配置了多个重做日志文件组,每个组包含一个或多个成员文件。这样做的目的是为了提供冗余,防止单个文件损坏导致整个数据库无法恢复。 - **写入过程**: - 当一个修改操作发生时,Oracle首先将...

    logrotate日志切割工具自动化脚本

    **日志管理在IT系统中扮演着至关重要的角色,它记录了系统运行的状态、错误信息以及各种活动,便于排查问题和进行性能分析。在Linux环境中,`logrotate`是一个非常实用的日志切割工具,用于自动化地管理和轮换日志...

    Oracle重做日志机制分析.pdf

    以下是对Oracle重做日志机制的详细分析: 1. **重做日志缓冲区** 重做日志缓冲区位于系统全局区(SGA)中,它是一个循环使用的内存区域,大小由参数`LOG_BUFFER`设定。这个缓冲区存放了数据修改前后的快照,包括...

    oracle重做日志文件管理.pptx

    一个日志组至少包含一个文件,而多文件日志组则允许LGWR后台进程同时写入多个文件,提高性能。查询重做日志文件组的信息可以通过SQL语句`SELECT group#, sequence#, members, bytes, status, archived FROM v$log`...

Global site tag (gtag.js) - Google Analytics