日志框架统一切换之(三):日志分割需求(转)

一、需求

每天的日志按照一天四份的分割,即每天的6点、12点、18点、24点分割日志。

二、现状

目前的logback只是按照每月、日、半天、时、分切割,没有按照每隔6小时切割的配置支持(我知道的没有,请指教)

三、解决方案

之前使用过SizeAndTimeBasedFNATP来按照时间和文件大小分割日志;需求相似性首先想到通过扩展这个类来满足需求。

四、实施步骤

1、SizeAndTimeBasedFNATP源码理解(这里只看了几个从父类集成来的方法)

start方法:策略初始化的方法,在启动时初始化相关属性和方法;
isTriggeringEvent方法:触发器是否触发方法(核心)
getCurrentPeriodsFileNameWithoutCompressionSuffix:获取当前时期文件名的方法
比较重要的是第二个isTriggeringEvent方法
@Override
  public void start() {
    // 通过父类的start方法初始化相关属性
    super.start();
    //和压缩相关的API,暂不关注
    archiveRemover = createArchiveRemover();
    archiveRemover.setContext(context);

    // we need to get the correct value of currentPeriodsCounter.
    // usually the value is 0, unless the appender or the application
    // is stopped and restarted within the same period
    //主要用于初始化currentPeriodsCounter这个变量,这个变量是用来计算在指定时间间隔内第几个文件
    String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod);
    String stemRegex = FileFilterUtil.afterLastSlash(regex);

    computeCurrentPeriodsHighestCounterValue(stemRegex);

    started = true;
  }
public boolean isTriggeringEvent(File activeFile, final E event) {
    //检测当前时间是否超过下一个时间监测点,
    //如果超过,计算文件名(elapsedPeriodsFileName)、重置当前时期属性(dateInCurrentPeriod)、
    //计算下一个时间监测点(nextCheck)
    long time = getCurrentTime();
    if (time >= nextCheck) {
      Date dateInElapsedPeriod = dateInCurrentPeriod;
      elapsedPeriodsFileName = tbrp.fileNamePatternWCS
              .convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter);
      currentPeriodsCounter = 0;
      setDateInCurrentPeriod(time);
      computeNextCheck();
      return true;
    }

    // for performance reasons, check for changes every 16,invocationMask invocations
    if (((++invocationCounter) & invocationMask) != invocationMask) {
      return false;
    }
    if (invocationMask < 0x0F) {
      invocationMask = (invocationMask << 1) + 1;
    }
    //文件大小检测,如果指定大小,增加currentPeriodsCounter
    if (activeFile.length() >= maxFileSize.getSize()) {
      elapsedPeriodsFileName = tbrp.fileNamePatternWCS
              .convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter);
      currentPeriodsCounter++;
      return true;
    }

    return false;
  }

2、解决思路

从上面的源码解读中,我很快注意到了两个成员变量:
    dateInCurrentPeriod:当前所处的时期
    nextCheck:下一次检测时间
如果能把nextCheck每次推后6个小时,是否就满足我的需求了呢。

3、具体实施

a、新增一个SizeAndTimeBasedFNATP的子类DemoSizeAndTimeBasedFNATP。
b、添加一个int型成员变量:multiple,用于配制每次添加几个单元时间
c、重写计算nextCheck的方法computeNextCheck如下:
     protected void computeNextCheck() {
        nextCheck = rc.getRelativeDate(dateInCurrentPeriod,multiple).getTime();
     }

经过上述三步,我的扩展类变成如下:

import java.util.Calendar;
import java.util.Date;

import ch.qos.logback.core.joran.spi.NoAutoStart;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;

@NoAutoStart
public class DemoSizeAndTimeBasedFNATP<E> extends SizeAndTimeBasedFNATP<E> {
  private Integer multiple = 1;
  protected void computeNextCheck() {
        nextCheck = rc.getRelativeDate(dateInCurrentPeriod,multiple).getTime();
  }
  public Integer getMultiple() {
    return multiple;
  }
  public void setMultiple(Integer multiple) {
    if(multiple>1){
        this.multiple = multiple;
    }    
  }  
}

将此类配制到logback.xml中

      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${filePrefix}/demo-error.%d{yyyyMMddHHmm}.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="com.demo.utils.MlsPaySizeAndTimeBasedFNATP">
                 <multiple>10</multiple>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>

经过以上配置,日志文件变成了每隔6分钟一个分割。

似乎已经完成了我的需求,但是很快我发现了一个问题,那就是:

我中间停机3分钟,再次启动,其切割时间会向后平推。这不符合需求每天6点、12点、18点、24点切割的需求。

很快意识到是在启动时,dateInCurrentPeriod和nextCheck两个变量值初始化的问题。

因此决定在start方法中重新校正一下这两个参数的值,让dateInCurrentPeriod在启动时,初始化为所处时段的初始时间;
让nextCheck初始为所处时段的结束时间。

具体代码如下:

@Override
  public void start() {
    super.start();
    initCurrentPeriod(multiple);
    initNextCheck(multiple);
  }
  
  private void initCurrentPeriod(Integer multiple){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        switch (rc.getPeriodicityType()) {
          case TOP_OF_MINUTE:
              calendar.set(Calendar.SECOND, 0);
              calendar.set(Calendar.MILLISECOND, 0);
              calendar.set(Calendar.MINUTE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple);
              break;
          case TOP_OF_HOUR:
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            calendar.set(Calendar.HOUR_OF_DAY,(calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple);
            break;

          case TOP_OF_DAY:
              calendar.set(Calendar.HOUR_OF_DAY, 0);
              calendar.set(Calendar.MINUTE, 0);
              calendar.set(Calendar.SECOND, 0);
              calendar.set(Calendar.MILLISECOND, 0);
              calendar.set(Calendar.DATE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple)*multiple);
            break;
          default:
            throw new IllegalStateException("不支持倍数增加的单位");
        }
        
        dateInCurrentPeriod = calendar.getTime();
  }
  
  private void initNextCheck(Integer multiple){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        switch (rc.getPeriodicityType()) {
          case TOP_OF_MINUTE:
              calendar.set(Calendar.SECOND, 0);
              calendar.set(Calendar.MILLISECOND, 0);
              calendar.set(Calendar.MINUTE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple);
              break;
          case TOP_OF_HOUR:
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            calendar.set(Calendar.HOUR_OF_DAY,(calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple);
            break;

          case TOP_OF_DAY:
              calendar.set(Calendar.HOUR_OF_DAY, 0);
              calendar.set(Calendar.MINUTE, 0);
              calendar.set(Calendar.SECOND, 0);
              calendar.set(Calendar.MILLISECOND, 0);
              calendar.set(Calendar.DATE, (calendar.get(Calendar.HOUR_OF_DAY)/multiple+1)*multiple);
            break;
          default:
            throw new IllegalStateException("不支持倍数增加的单位");
        }
        
        nextCheck = calendar.getTime().getTime();
  }

经过以上配置,再将logback中的配置改为每2小时分割,部署到测试环境,目前已经跑了一天了,看起来一切正常。

以上只是我个人的解决方法,对于logback了解不深,猜想应该有更完美的解决方案,希望各位不吝赐教和指正

http://my.oschina.net/bayuanqian/blog/205315

时间: 2024-10-10 15:37:38

日志框架统一切换之(三):日志分割需求(转)的相关文章

JAVAEE——SpringBoot日志篇:日志框架SLF4j、日志配置、日志使用、切换日志框架

Spring Boot 日志篇 1.日志框架(故事引入) 小张:开发一个大型系统: ? 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? ? 2.框架来记录系统的一些运行时信息:日志框架 : zhanglogging.jar: ? 3.高大上的几个功能?异步模式?自动归档?xxxx? zhanglogging-good.jar? ? 4.将以前框架卸下来?换上新的框架,重新修改之前相关的API:zhanglogging-prefect.ja

springBoot日志框架自动配置与原理

1.日志框架 小张:开发一个大型系统: ? 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? ? 2.框架来记录系统的一些运行时信息:日志框架 : zhanglogging.jar: ? 3.高大上的几个功能?异步模式?自动归档?xxxx? zhanglogging-good.jar? ? 4.将以前框架卸下来?换上新的框架,重新修改之前相关的API:zhanglogging-prefect.jar: ? 5.JDBC---数据库驱动: ?

Spring集成Log4j日志框架

1.日志系统介绍 slf4j,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统.简答的讲就是slf4j是一系列的日志接口,而log4j是具体实现了的日志框架. slf4j与常用日志框架绑定关系,图片来源 2.Maven导入slf4j和log4j,编辑pom.xml <!-- 导入slf4j-log4j12,依赖slf4j-api和log4j,自动导入 --> <dependency> <gro

IDEA整合日志框架Log4j2+Slf4j详细配置过程

日志框架这么多,他们之间到底是什么关系呢?笼统的讲就是slf4j是一系列的日志接口,而log4j2.logback是具体实现了接口功能的日志框架.现在的主流日志接口都使用slf4j,而日志的实现就见仁见智了,至于他们的关系请自行百度,此处选择log4j2作为实现框架.网上看到的教程要么对代码没有解释,对新手不友好:要么时间比较久远,跟不上时代.这里使用新版本并结合大量注释,力求简洁明了,有什么问题欢迎留言交流. 运行环境: log4j2 2.8.1 + slf4j 1.7.25 IntelliJ

Exceptionless - .Net Core开源日志框架

原文:Exceptionless - .Net Core开源日志框架 Exceptionless - .Net Core开源日志框架 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/11020140.html 官网地址:http://letyouknow.net 今天要给大家介绍的Exceptionless是一个基于 .net core的开源日志框架,Exceptionless的意思是:没有异常.Exceptionless可

SQL Server中的事务日志管理(7/9):处理日志过度增长

当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会告诉你每个DBA应该知道的具体细节. 这篇文章会列出导致事务日志过度增长的常见的问题和错误管理形式,包括: 在完整恢复模式里,没有进行日志备份 进行索引维护 长时间运行或未提交的事务阻止事务日志里空间重用 当然,如果增长没检查,日志文件会扩展直到吞没所有可用磁盘空间或日志文件的最大大小,在这个时候你

三、日志框架

1.市面上的日志框架 JUL.JCL.Jboss-logging.logback.log4j.log4j2.slf4j.... 日志门面 (日志的抽象层) 日志实现 JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback SpringBoot:底层是Spring框架,Spring框架默认是用JCL: Sp

Java日志体系 —— 日志框架切换

通过 SLF4J 统一日志 在实际的日志转换过程中,SLF4J其实是充当了一个中介的角色.例如当我们一个项目原来是使用LOG4J进行日志记录,但是我们要换成LogBack进行日志记录. 此时我们需要先将LOG4J转换成SLF4J日志系统,再从SLF4J日志系统转成LogBack日志系统. 从日志框架转向SLF4J jul-to-slf4j:jdk-logging到slf4j的桥梁 log4j-over-slf4j:log4j1到slf4j的桥梁 jcl-over-slf4j:commons-lo

SpringBoot与日志框架2(日志内斗)

一.SpringBoot如何引入slf4j+logback框架的呢? 在POM文件中 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>依赖 <dependency> <groupId>org.springframework.